6

ile çalışır değildir:C++ işaretçi-to-yöntemde şablon kesinti x86 hedeflerken derlemek, ama bu örnek kod var 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; 
} 

Bu g++ ile iyi derler. Aynı zamanda VC10 ile iyi derler, ancak sadece 64 bit platform için inşa ederken. Ben 32 bit platformu için oluştururken, bu derleme hatası alıyorum:

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

hata Gobstopper olmayan uzman versiyonu demek olmalı, hangi kullanıldığını gösterir Signatureint (void) diğer bir şeydir. Ancak bu hata açıkça Signature'unint (void) olduğunu söylüyor. Peki hata nereden geliyor? Ve bunu nasıl düzeltebilirim?

Bunu düşünebildiğim tek şey, 32-bit'ten 64-bit'e değişebilir ve hata iletisinde görüntülenen imzada görünmüyorsa çağrı kuralıdır; görünüşte, there is a unified calling convention for VC x64, whereas for x86 each calling convention is distinct. Ama sorun olsa bile, nasıl düzelteceğimi bilmiyorum.

Yardım!

Düzenleme: Bunu normal (üye olmayan) işlev işaretçileriyle denediğimi ve bunun iyi çalıştığını belirtmeliyim.

cevap

5

Haklısınız. Bir Win32 hedefi olan Signature türünün int __thiscall(void), x64 üzerinde ise int __cdecl(void) şeklindedir. Her iki hedefte de, üye olmayan işlevlerin (genellikle int(void)) gerçekte int __cdecl(void) olduğunu, dolayısıyla tesadüf esasında türlerin (gerçekten doğru değil!) Eşleştiğini unutmayın.

Genelde, farklı işlev işaretleyicilerini şablon büyüsü ile karıştırmanız önerilmez, bu nedenle Gobstopper uzmanlığı bunun yerine int (ClassType::*)() gibi bir şeye bakmalıdır.

+0

Hmm. Şüphelerimi onayladığınız için teşekkür ederiz (bu arada, nasıl kontrol ettiniz? Şablon argümanlarını ayıklamak oldukça zor buluyorum). Çağrı sözleşmesini "İmza" dan ayırabilmemin herhangi bir yolu var mı? Aslında bir fonksiyon işaretçisini tanımlamak için Gobstopper içinde "T" yi kullanmıyorum, sadece şablon uzmanlıklarını ayırt etmenin bir aracı olarak. Ayrıca, nitpick için (Tamam, nitpick için), ama x64 '__thiscall' da çağrı kuralı değil, ama '__cdecl' ile uyumlu olması umulur mu? – Cameron

+0

Aslında, VC++ için 32 bitlik kural, "normal" işlevler için cdecl ve bu işlev üye işlevleri içindir. 64 bit Windows'ta, 32 bit ortamdan ne cdecl ne de thiscall olarak bilinir, fakat her iki tür için de daha fazla yazmaç kullanır. Daha fazla bilgi edinmek isterseniz, http://www.agner.org/optimize/calling_conventions.pdf adresinde bulunan ve hemen hemen her arama kuralı listelenmektedir. –

+0

Oh ve VS üzerinde, bir türün ismini vermek için 'typeid (T) .name()' yi kullanabilirsiniz - şablon argümanı olsa bile. –

İlgili konular