在std::unique_ptr的删除器中是否需要使用__stdcall?
为何MSVC编译32位和64位时std::unique_ptr函数指针行为不同?
尝试用std::unique_ptr管理带__stdcall调用约定的函数对,代码如下:
#include <memory> int* __stdcall construct(){ return new int{5}; } void __stdcall destruct(int* v){ delete v; } int main() { using Ptr = std::unique_ptr<int, void(&)(int*)>; Ptr v(construct(), destruct); }
使用MSVC 19.33编译64位二进制可正常运行,但编译32位时报错:
example.cpp <source>(8): error C2660: 'std::unique_ptr<int,void (__cdecl &)(int *)>::unique_ptr': function does not take 2 arguments C:/data/msvc/14.33.31631/include\memory(3291): note: see declaration of 'std::unique_ptr<int,void (__cdecl &)(int *)>::unique_ptr' Compiler returned: 2
若在std::unique_ptr的删除器模板参数中显式指定__stdcall(即std::unique_ptr<int, void(__stdcall&)(int*)>),则32位与64位编译均可正常运行。
原因分析
这不是编译器bug,而是32位和64位Windows平台对调用约定的处理差异导致:
- 64位Windows平台:仅存在一种通用调用约定(微软定义的x64调用约定,等价于
__fastcall),__stdcall、__cdecl等关键字在64位编译时会被忽略,所有函数指针统一使用该调用约定。因此void(&)(int*)会自动匹配__stdcall函数的类型,不会出现类型不匹配问题。 - 32位Windows平台:
__stdcall和__cdecl是两种完全不同的调用约定,函数指针类型会严格区分调用约定。当你写void(&)(int*)时,MSVC默认将其推导为__cdecl的函数引用类型,但你的destruct函数是__stdcall类型,两者类型不兼容。而std::unique_ptr没有对应这种类型不匹配参数的构造函数重载,因此触发编译错误。
显式指定__stdcall后,删除器的引用类型与实际函数的调用约定完全一致,32位编译时就能正确匹配构造函数重载。
内容的提问来源于stack exchange,提问作者Benjamin Buch
相关产品推荐
相关产品推荐

