Вывод шаблона указателя на метод С++ не компилируется при нацеливании на x86, но работает с x64

У меня есть этот пример кода:

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

Это прекрасно компилируется с g++. Он также отлично компилируется с VC10, но только при сборке для 64-битной платформы. Когда я строю для 32-битной платформы, я получаю эту ошибку компиляции:

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>          ]

Ошибка указывает на то, что используется неспециализированная версия Gobstopper, что должно означать, что Signature отличается от int (void). Но ошибка также ясно говорит, что Signature является int (void). Так откуда ошибка? И как я могу это исправить?

Единственное, что я могу придумать, что может измениться с 32-битной на 64-битную и не отображаться в подписи, отображаемой в сообщении об ошибке, - это соглашение о вызовах; по-видимому, существует унифицированное соглашение о вызовах для VC x64, тогда как для x86 каждое соглашение о вызовах отличается. Но даже если это проблема, я понятия не имею, как это исправить.

Изменить: я должен упомянуть, что я пробовал это с обычными (не членами) указателями функций, и это сработало нормально.




Ответы (1)


Вы совершенно правы. Тип Signature с целью Win32 — int __thiscall(void), а на x64 — int __cdecl(void). Обратите внимание, что в любой цели тип функций, не являющихся членами, обычно называемый int(void), на самом деле является int __cdecl(void), поэтому по совпадению один из сконструированных типов фактически (не совсем правильно!) совпадает.

В общем, не рекомендуется смешивать различные типы указателей функций с помощью магии шаблонов, поэтому вместо этого специализация Gobstopper должна выглядеть как int (ClassType::*)().

person gha.st    schedule 15.03.2012
comment
Хм. Спасибо, что подтвердили мои подозрения (кстати, как вы проверяли? Мне довольно сложно отлаживать аргументы шаблона). Можно ли как-то отделить соглашение о вызовах от Signature? На самом деле я не использую T в Gobstopper для определения указателя функции, просто как средство устранения неоднозначности специализаций шаблона. Кроме того, не придираться (окей, не придираться), но разве соглашение о вызовах для x64 тоже не __thiscall, но оно совместимо с __cdecl? - person Cameron; 16.03.2012
comment
На самом деле, 32-битное соглашение для VC++ — cdecl для обычных функций и thiscall для функций-членов. В 64-битной Windows это не cdecl и не thiscall, известные из 32-битной среды, а нечто другое, использующее даже больше регистров, чем thiscall для обоих типов. Если вы хотите узнать больше, обратитесь на agner.org/optimize/calling_conventions.pdf где перечислены почти все используемые соглашения о вызовах. - person gha.st; 16.03.2012
comment
О, и в VS вы можете использовать typeid(T).name(), чтобы дать вам имя типа, даже если это аргумент шаблона. - person gha.st; 16.03.2012
comment
О, гениально, спасибо! Я просто хочу, чтобы был способ игнорировать или как-то изменить соглашение о вызовах Signature... - person Cameron; 16.03.2012
comment
К сожалению, соглашение о вызовах - это еще не все... Стандарт делает функции-члены (и указатели на члены) очень отличными от не-членов, поэтому еще раз мой совет - даже не пытаться это смешивание;) - person gha.st; 16.03.2012
comment
Да, я понимаю. Спасибо за вашу помощь! - person Cameron; 16.03.2012