C++ pointer-to-method template deduction doesn'

2020-03-02 20:01发布

问题:

I've got this sample code:

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

This compiles fine with g++. It also compiles fine with VC10, but only when building for the 64-bit platform. When I build for the 32-bit platform, I get this compile error:

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

The error indicates that the non-specialized version of Gobstopper is being used, which must mean the Signature is something other that int (void). But the error also clearly says that Signature is int (void). So where does the error come from? And how can I fix it?

The only thing I can think of that might change from 32-bit to 64-bit and not show up in the signature displayed in the error message is the calling convention; apparently, there is a unified calling convention for VC x64, whereas for x86 each calling convention is distinct. But even if that's the problem, I have no idea how to fix it.

Edit: I should mention that I tried this with regular (non-member) function pointers, and that worked fine.

回答1:

You are quite correct. The type of Signature with a Win32 target is int __thiscall(void) while on x64 it is int __cdecl(void). Note that on either target the type of non-member functions commonly called int(void) is indeed int __cdecl(void) so, by coincidence one of the constructed types actually (not really correctly!) matches.

In general it is not advisable to mix the different types of function pointers by template magic, so the Gobstopper specialization should look at something like int (ClassType::*)() instead.