6

Tôi đã có mẫu mã này:C++ template khấu trừ con trỏ-to-phương pháp không biên dịch khi nhắm mục tiêu x86, nhưng hoạt động với 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; 
} 

này biên dịch tốt với g++. Nó cũng biên dịch tốt với VC10, nhưng chỉ khi xây dựng nền tảng 64 bit. Khi tôi xây dựng cho nền tảng 32-bit, tôi nhận được lỗi biên dịch này:

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

Các lỗi chỉ ra rằng phiên bản không chuyên của Gobstopper đang được sử dụng, trong đó phải có nghĩa là Signature là một cái gì đó khác mà int (void). Nhưng lỗi cũng nói rõ ràng rằng Signatureint (void). Vậy lỗi đó đến từ đâu? Và làm thế nào tôi có thể sửa nó?

Điều duy nhất tôi có thể nghĩ về điều đó có thể thay đổi từ 32-bit thành 64-bit và không hiển thị trong chữ ký được hiển thị trong thông báo lỗi là quy ước gọi; hiển nhiên, there is a unified calling convention for VC x64, whereas for x86 each calling convention is distinct. Nhưng ngay cả khi đó là vấn đề, tôi không có ý tưởng làm thế nào để sửa chữa nó.

Trợ giúp!

Chỉnh sửa: Tôi nên đề cập đến rằng tôi đã thử điều này với con trỏ hàm thường xuyên (không phải thành viên) và hoạt động tốt.

Trả lời

5

Bạn khá chính xác. Loại Signature với mục tiêu Win32 là int __thiscall(void) trong khi trên x64 là int __cdecl(void). Lưu ý rằng trên một trong hai mục tiêu, loại hàm không phải là thành viên thường được gọi là int(void) thực sự là int __cdecl(void) vì vậy, trùng hợp một trong các loại kết hợp thực sự (không thực sự chính xác!) Phù hợp.

Nói chung, không nên kết hợp các loại con trỏ hàm khác nhau theo mẫu ma thuật, do đó, chuyên môn Gobstopper nên nhìn vào thứ gì đó như int (ClassType::*)() thay thế.

+0

Hmm. Cảm ơn bạn đã xác nhận sự nghi ngờ của tôi (làm thế nào bạn kiểm tra, bằng cách này? Tôi đang tìm kiếm nó khá khó khăn để gỡ lỗi các đối số mẫu). Có cách nào tôi có thể tách các quy ước gọi từ 'Chữ ký'? Tôi không thực sự sử dụng 'T' trong Gobstopper để định nghĩa một con trỏ hàm, chỉ đơn thuần là một phương tiện định hướng các chuyên môn khuôn mẫu. Ngoài ra, không phải để nitpick (OK, để nitpick), nhưng không phải là quy ước gọi trên x64 '__thiscall' là tốt, nhưng nó sẽ xảy ra để tương thích với' __cdecl'? – Cameron

+0

Thực ra, quy ước 32 bit cho VC++ là cdecl cho các hàm "bình thường" và hàm này cho các hàm thành viên. Trên Windows 64 bit, nó không phải là cdecl hay thiscall được biết đến từ môi trường 32 bit, nhưng một cái gì đó khác nhau bằng cách sử dụng nhiều thanh ghi hơn so với thiscall cho cả hai loại. Nếu bạn muốn biết nhiều hơn, hãy xem xét tư vấn http://www.agner.org/optimize/calling_conventions.pdf nơi có nhiều quy ước gọi điện đang sử dụng được liệt kê. –

+0

Ồ, và trên VS bạn có thể sử dụng 'typeid (T) .name()' để cung cấp cho bạn tên của một loại - ngay cả khi nó là một đối số mẫu. –

Các vấn đề liên quan