2010-04-09 9 views
15

Observé algunas inconsistencias entre dos compiladores (g ++ 4.5, VS2010 RC) en la forma en que combinan lambdas con especializaciones parciales de plantillas de clase. Estaba intentando implementar algo como boost :: function_types para lambdas para extraer rasgos de tipo. Compruebe this para más detalles.incoherencia de rasgos lambda en compiladores C++ 0x

En g ++ 4.5, el tipo de operator() de una lambda parece ser como el de una función independiente (R (*) (...)) mientras que en VS2010 RC, parece ser como la de un función miembro (R (C :: *) (...)). Entonces, la pregunta es si los escritores de compiladores pueden interpretar de la manera que quieran. Si no, ¿qué compilador es el correcto? Vea los detalles a continuación.

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

Este programa compila en tanto g ++ 4.5 y VS2010 pero los function_traits que se crean instancias son diferentes como se indica en el código.

Respuesta

4

Creo que GCC no cumple. N3092 §5.1.2/5 dice

El tipo de cierre para un lambda-expresión tiene una línea operador de llamada de función pública (13.5.4) cuya pará- metros y volver tipo son descrito por el lambda-expression's parameter-declaration-clause y trailing-return-type respectivamente. Este operador de llamada a función es declarado const (9.3.1) si y solo si lambda- expression parameter-declaration-clause no es seguido de mutable.

Así, mientras que muchas cosas sobre el tipo de objeto de cierre son definido por la implementación, la función en sí debe ser un miembro para ser public y debe ser un miembro que no es para ser const.

EDIT: Este programa indica que operator() es una función miembro en GCC 4.6, que es esencialmente lo mismo que 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; 
} 

de salida:

member of Z4mainEUlvE_, returns i takes i 

EDIT: Parece que el único problema es que los punteros a determinados operadores de llamadas de cierre no coinciden patrones plantilla pTMF. La solución consiste en declarar la expresión lambda mutable. Esto no tiene sentido si no hay captura y solo (aparte de solucionar el problema) parece cambiar la constidad del operador de llamada.

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; 

de salida:

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

Sin la mutable y con la const, spectfun no imprime firmas para cualquiera de las dos últimas consultas.

+1

¿No haría eso que g ++ no cumpliera, ya que usa funciones libres? – GManNickG

+0

Sí, me parece que es GCC el que está equivocando. – jalf

+0

@Gman, jalf: Bah, audición selectiva. Bueno, el método de GCC implica más amistad ... ¡ja! – Potatoswatter

1

Lee n3043. Los Lambdas ahora son convertibles a punteros de función siempre que no tengan ningún estado. Creo (... pero no lo sé) que GCC implementó inicialmente este comportamiento accidentalmente, lo "arregló", ahora lo volverá a agregar a 4.5 o 4.6.VC10 implementó lambdas correctamente como se diseñó inicialmente, pero no se ajusta a los últimos documentos de trabajo con n3043.

+0

n3092 es el último borrador: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf [Enlace PDF] – GManNickG

+0

Sí, lo es, pero estoy hablando de n3043 específicamente. –

+0

Aunque las lambdas sin estado son convertibles en punteros a función, cuando se encuentra una coincidencia mejor R (C :: *) (...), se debe elegir, ¿no es así? – Sumant

0

Creo que los desarrolladores de gcc tienen una buena razón para este comportamiento. Recuerde, una función estática no tiene un puntero "this", y cuando realmente se lo llama, la persona que llama no necesita pasar el puntero "this". Así que esta es una optimización de rendimiento pequeña cuando en realidad no es nada contenido en el objeto de cierre. Y puede ver que el desarrollador de G ++ le deja un camino a la solución al declarar la expresión lambda como "mutable" (recuerde que en realidad no tiene nada que cambiar).

Cuestiones relacionadas