6

Tengo este código de ejemplo:C++ deducción plantilla-puntero a método no compila cuando la orientación x86, x64, pero trabaja con

struct A 
{ 
    int foo() { return 27; } 
}; 

template<typename T> 
struct Gobstopper 
{ 
}; 

template<> 
struct Gobstopper<int(void)> 
{ 
    Gobstopper(int, int) { } // To differentiate from general Gobstopper template 
}; 

template<typename ClassType, typename Signature> 
void DeduceMethodSignature(Signature ClassType::* method, ClassType& instance) 
{ 
    // If Signature is int(), Gobstopper<> should resolve to the specialized one. 
    // But it only does on x64! 
    Gobstopper<Signature>(1, 2); 
} 

int main(int argc, char** argv) 
{ 
    A a; 
    DeduceMethodSignature(&A::foo, a); 

    return 0; 
} 

Este compila bien con g++. También compila bien con VC10, pero solo cuando se compila para la plataforma de 64 bits. Cuando construyo para la plataforma de 32 bits, consigo este error de compilación:

error C2661: 'Gobstopper<T>::Gobstopper' : no overloaded function takes 2 arguments 
1>   with 
1>   [ 
1>    T=int (void) 
1>   ] 
1>   c:\...\test.cpp(26) : see reference to function template instantiation 'void DeduceMethodSignature<A,int(void)>(Signature (__thiscall A::*),ClassType &)' being compiled 
1>   with 
1>   [ 
1>    Signature=int (void), 
1>    ClassType=A 
1>   ] 

El error indica que se está utilizando la versión no especializado de chicle, que debe significar la Signature es algo más que int (void). Pero el error también dice claramente que Signatureesint (void). Entonces, ¿de dónde viene el error? ¿Y cómo puedo solucionarlo?

Lo único que se me ocurre que podría cambiar de 32 bits a 64 bits y no aparecer en la firma que se muestra en el mensaje de error es la convención de llamadas; aparentemente, there is a unified calling convention for VC x64, whereas for x86 each calling convention is distinct. Pero incluso si ese es el problema, no tengo idea de cómo solucionarlo.

¡Ayuda!

Editar: Debo mencionar que probé esto con punteros de función regulares (no miembros), y que funcionó bien.

Respuesta

5

Estás en lo cierto. El tipo de Signature con un objetivo Win32 es int __thiscall(void) mientras que en x64 es int __cdecl(void). Tenga en cuenta que en cualquiera de los objetivos, el tipo de funciones no miembro comúnmente llamado int(void) es de hecho int __cdecl(void), por lo que, por coincidencia, uno de los tipos construidos en realidad (¡no realmente!) Coincide.

En general, no es aconsejable mezclar los diferentes tipos de punteros de función por magia de plantilla, por lo que la especialización de Gobstopper debería tener un aspecto similar a int (ClassType::*)().

+0

Hmm. Gracias por confirmar mis sospechas (¿cómo revisaste, por cierto? Me parece bastante complicado depurar argumentos de plantilla). ¿Hay alguna manera de separar la convención de llamadas de 'Firma'? Realmente no uso 'T' dentro de Gobstopper para definir un puntero a la función, simplemente como un medio para desambiguar las especializaciones de las plantillas. Además, no para criticar (OK, para Nitpick), pero tampoco es la convención de llamadas en x64 '__thiscall', pero pasa a ser compatible con' __cdecl'? – Cameron

+0

En realidad, la convención de 32 bits para VC++ es cdecl para funciones "normales" y esto llama para funciones de miembros. En Windows de 64 bits, ni el cdecl ni el llamado son conocidos del entorno de 32 bits, pero hay algo diferente que utiliza incluso más registros que este llamado para ambos tipos. Si desea obtener más información, considere consultar http://www.agner.org/optimize/calling_conventions.pdf donde se enumeran prácticamente todas las convenciones de llamadas en uso. –

+0

Ah, y en VS puede usar 'typeid (T) .name()' para darle el nombre de un tipo, incluso si es un argumento de plantilla. –

Cuestiones relacionadas