Frage

beobachtete ich eine gewisse Inkonsistenz zwischen zwei Compiler (g ++ 4.5, VS2010 RC) in der Art, wie sie lambdas mit teilweise Spezialisierungen der Klasse Vorlagen entsprechen. Ich habe versucht, so etwas wie boost :: function_types für Lambda-Ausdrücke, um Extrakt Typ Züge zu implementieren. Überprüfen Sie diese für weitere Details.

in g ++ 4.5, der Typ des operator() einer Lambda wie die von einer freistehenden Funktion zu sein scheint (R (*) (...)), wohingegen in VS2010 RC, erscheint es so, dass ein Mitglied zu sein, Funktion (R (C :: *) (...)). Die Frage ist also sind Compiler Autoren frei, jede Art, wie sie wollen, zu interpretieren? Wenn nicht, welche Compiler ist richtig? Siehe die Details unten.

template <typename T>
struct function_traits 
  : function_traits<decltype(&T::operator())> 
{ 
// This generic template is instantiated on both the compilers as expected.
};

template <typename R, typename C>
struct function_traits<R (C::*)() const>  { // inherits from this one on VS2010 RC
  typedef R result_type;
};

template <typename R>
struct function_traits<R (*)()> { // inherits from this one on g++ 4.5
  typedef R result_type;
};

int main(void) {
  auto lambda = []{};
  function_traits<decltype(lambda)>::result_type *r; // void *
}

Dieses Programm kompiliert sowohl g ++ 4.5 und VS2010 aber die function_traits die instanziiert werden, sind verschieden, wie in dem Code angegeben.

War es hilfreich?

Lösung

Ich glaube, dass GCC noncompliant ist. N3092 §5.1.2 / 5 sagt

  

Der Verschlusstyp für ein   Lambda-Ausdruck hat eine öffentliche Inline   Funktionsaufruf Operator (13.5.4), deren   Para- meter und Rückgabetyp sind   durch die Lambda-Ausdruck des beschriebenen   Parameter-Deklaration-Klausel und   Nachlauf- Rückgabetyp ist.   Dieser Funktionsaufruf Operator   erklärt const (9.3.1), wenn und nur wenn   der Lambda- Ausdruck ist   Parameter-Deklaration-Klausel ist nicht   gefolgt von wandelbar.

Während also viele Dinge über das Typen des Verschlussobjekts sind die Implementierung definiert, die Funktion selbst muss Mitglied sein public zu sein und muss ein nicht-statisches Mitglied seines const zu sein.

EDIT:. Dieses Programm zeigt an, dass operator() ist eine Memberfunktion auf GCC 4.6, die als 4,5 im wesentlichen das gleiche ist

#include <iostream>
#include <typeinfo>
using namespace std;

template< class ... > struct print_types {};

template<> struct print_types<> {
 friend ostream &operator<< ( ostream &lhs, print_types const &rhs ) {
  return lhs;
 }
};

template< class H, class ... T > struct print_types<H, T...> {
 friend ostream &operator<< ( ostream &lhs, print_types const &rhs ) {
  lhs << typeid(H).name() << " " << print_types<T...>();
  return lhs;
 }
};

template< class T >
struct spectfun {
 friend ostream &operator<< ( ostream &lhs, spectfun const &rhs ) {
  lhs << "unknown";
  return lhs;
 }
};

template< class R, class ... A >
struct spectfun< R (*)( A ... ) > {
 friend ostream &operator<< ( ostream &lhs, spectfun const &rhs ) {
  lhs << "returns " << print_types<R>()
   << " takes " << print_types<A ...>();
  return lhs;
 }
};

template< class C, class R, class ... A >
struct spectfun< R (C::*)( A ... ) > {
 friend ostream &operator<< ( ostream &lhs, spectfun const &rhs ) {
  lhs << "member of " << print_types<C>() << ", " << spectfun<R (*)(A...)>();
  return lhs;
 }
};

template< class T >
struct getcall {
 typedef decltype(&T::operator()) type;
};

int main() {
 int counter = 0;

 auto count = [=]( int ) mutable { return ++ counter; };

 cerr << spectfun< getcall<decltype(count)>::type >() << endl;
}

Ausgabe:

member of Z4mainEUlvE_, returns i takes i

EDIT: Es sieht aus wie das einzige Problem ist, dass Hinweise auf bestimmten Betreiber Schließung Aufruf fehlschlagen PTMF Schablonenmuster passen. Die Abhilfe ist die Lambda-Ausdruck mutable zu erklären. Das ist sinnlos, wenn es keine Erfassung und nur (abgesehen von der Behebung des Problems) scheint die const-ness der Call-Betreiber zu ändern.

template< class T >
struct getcall {
    typedef decltype(&T::operator()) type;
    static type const value;
};
template< class T >
typename getcall<T>::type const getcall<T>::value = &T::operator();

int main() {
    auto id = []( int x ) mutable { return x; };
    int (*idp)( int ) = id;
    typedef decltype(id) idt;
    int (idt::*idptmf)( int ) /* const */ = getcall< decltype(id) >::value;

cerr << spectfun< decltype(idp) >() << endl;
cerr << spectfun< decltype(idptmf) >() << endl;
cerr << spectfun< getcall<decltype(id)>::type >() << endl;

Ausgabe:

returns i takes i 
member of Z4mainEUliE0_ , returns i takes i 
member of Z4mainEUliE0_ , returns i takes i 

Ohne die wandelbar und mit dem const, spectfun druckt nicht Unterschriften für eine der beiden letzten zwei Abfragen.

Andere Tipps

Lesen Sie n3043 . Lambda-Ausdrücke sind jetzt umwandelbar Funktionszeiger, vorausgesetzt, sie haben keinen Staat. Ich glaube, (... aber nicht wissen) GCC zunächst dieses Verhalten zufällig umgesetzt „es fest“, wird nun wieder Zugabe zu 4,5 oder 4,6 sein. VC10 realisierte lambdas korrekt wie ursprünglich entworfen, aber die nicht die neuesten Arbeitspapiere mit n3043.

Ich denke, gcc Entwickler hat einen guten Grund für diese behaivor. Denken Sie daran, eine statische Funktion nicht hat „diesen“ Zeiger, und wenn es tatsächlich aufgerufen wird, kann der Anrufer nicht passiert den „diesen“ Zeiger erforderlich. So ist dies eine kleine Performance-Optimierung ist, wenn es tatsächlich im Verschluß Objekt enthielt nichts. Und Sie können die G ++ Entwickler verlassen Sie einen Weg, um Abhilfe sehen durch den Lambda-Ausdruck als „wandelbar“ erklärt (erinnern Sie sich eigentlich nichts zu mutieren haben).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top