2010-04-09 10 views
15

Zaobserwowałem niespójność między dwoma kompilatorami (g ++ 4.5, VS2010 RC) w sposób, w jaki pasują one do lambdas z częściowymi specjalizacjami szablonów klas. Próbowałem zaimplementować coś w stylu boost :: function_types dla lambdas, aby wyodrębnić cechy typu. Sprawdź numer this, aby uzyskać więcej informacji.Niespójność cech lambda w kompilatorach C++ 0x

W g ++ 4.5, typ operator() z lambda wydaje się być podobny do funkcji wolnostojącej (R (*) (...)), podczas gdy w VS2010 RC, wydaje się, że jest podobny do funkcja składowa (R (C :: *) (...)). Pytanie brzmi: czy twórcy kompilatorów mają swobodę interpretowania w dowolny sposób? Jeśli nie, który kompilator jest poprawny? Zobacz szczegóły poniżej.

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 * 
} 

Ten program kompiluje zarówno na g ++ 4.5 i VS2010 ale function_traits które są wystąpienia są różne, jak wspomniano w kodzie.

Odpowiedz

4

Uważam, że GCC jest niezgodny. N3092 §5.1.2/5 mówi

typ zamknięcia dla lambda-ekspresyjnych publicznego rolki dla obsługi funkcji (13.5.4), którego pa- Zastosowano cewniki i powrót typu są opisane przez lambda-expression's parametr-deklaracja-klauzula i odpowiednio - zwrotny typ. Ten operator wywołania funkcji jest zadeklarowane const (9.3.1) wtedy i tylko wtedy, gdy nie parametr deklaracja-klauzuli lambda ekspresji jest następnie zmienny.

więc wiele rzeczy o zamknięcie typu obiektu są zdefiniowane w implementacji, sama funkcja musi być zarejestrowany jako public i musi być nonstatic member aby być const.

EDIT: Program ten wskazuje, że operator() jest funkcja składowa na GCC 4.6, który jest zasadniczo taki sam jak 4,5.

#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; 
} 

wyjściowa:

member of Z4mainEUlvE_, returns i takes i 

EDIT: Wygląda na to, jedynym problemem jest to, że wskaźniki do niektórych operatorów telefonicznych zamknięcie nie pasuje ptmf wzorów szablonów. Rozwiązaniem jest zadeklarowanie wyrażenia lambda mutable. Nie ma to znaczenia, jeśli nie ma przechwytywania i tylko (poza rozwiązaniem problemu) wydaje się zmieniać konstelację operatora połączenia.

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; 

wyjściowa:

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

Bez zmienny iz const, spectfun nie drukuje podpisów dla jednego z dwóch ostatnich zapytaniami.

+1

Czy nie byłoby to niezgodne z g ++, ponieważ korzysta z darmowych funkcji? – GManNickG

+0

Tak, dla mnie brzmi to tak, jakby to GCC było źle. – jalf

+0

@ Gman, jalf: Bah, selektywne przesłuchanie. Cóż, metoda GCC wiąże się z większą przyjaźnią ... ha! – Potatoswatter

1

Przeczytaj n3043. Lambdas są teraz zamieniane na wskaźniki funkcji, pod warunkiem, że nie mają żadnego stanu. Wierzę (... ale nie wiem) GCC początkowo zaimplementowało to zachowanie przypadkowo, "naprawił to", teraz ponownie doda je do 4.5 lub 4.6.VC10 zaimplementował lambdy poprawnie, jak pierwotnie zaprojektowano, ale nie spełniające wymagań najnowszych dokumentów roboczych z n3043.

+0

n3092 to najnowszy projekt: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf [link PDF] – GManNickG

+0

Tak, jest - ale mówię o w szczególności n3043. –

+0

Nawet jeśli bezpaństwowcze lambdy są zamienialne na wskaźniki funkcji, gdy lepszy układ R (C :: *) (...) jest obecny, należy go wybrać, czyż nie? – Sumant

0

Myślę, że deweloperzy gcc mają dobry powód do tego behaivor. Pamiętaj, że funkcja statyczna nie ma "tego" wskaźnika, a kiedy jest rzeczywiście wywoływana, wywołujący nie musi przekazywać "tego" wskaźnika. Jest to więc niewielka optymalizacja wydajności, gdy w rzeczywistości nie zawiera się w obiekcie zamknięcia. I widzisz, że deweloper G ++ daje ci sposób obejścia tego problemu, deklarując wyrażenie lambda jako "zmienne" (pamiętaj, że właściwie nie masz nic do mutacji).