Lambda-Züge Unstimmigkeit über C ++ 0x-Compiler
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.
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).