带__declspec(dllexport)的C++代码仅在MSVC编译失败的原因咨询
问题:MSVC导出含
std::unique_ptr的结构体时编译失败 代码片段
#include<memory> #include<unordered_map> struct __declspec(dllexport) Foo { std::unordered_map<const int*, std::unique_ptr<int>> foo; }; Foo foo();
编译错误信息
C:/data/msvc/14.39.33321-Pre/include\list(1299): error C2679: binary '=': no operator found which takes a right-hand operand of type 'const std::pair<const int *const ,std::unique_ptr<int,std::default_delete<int>>>' (or there is no acceptable conversion) C:/data/msvc/14.39.33321-Pre/include\utility(315): note: could be 'std::pair<_Kty,_Ty> &std::pair<_Kty,_Ty>::operator =(volatile const std::pair<_Kty,_Ty> &)' with [ _Kty=const int *, _Ty=std::unique_ptr<int,std::default_delete<int>> ] C:/data/msvc/14.39.33321-Pre/include\list(1299): note: 'std::pair<_Kty,_Ty> &std::pair<_Kty,_Ty>::operator =(volatile const std::pair<_Kty,_Ty> &)': cannot convert argument 2 from 'const std::pair<const int *const ,std::unique_ptr<int,std::default_delete<int>>>' to 'volatile const std::pair<_Kty,_Ty> &' with [ _Kty=const int *, _Ty=std::unique_ptr<int,std::default_delete<int>> ] C:/data/msvc/14.39.33321-Pre/include\list(1299): note: Reason: cannot convert from 'const std::pair<const int *const ,std::unique_ptr<int,std::default_delete<int>>>' to 'volatile const std::pair<_Kty,_Ty>' with [ _Kty=const int *, _Ty=std::unique_ptr<int,std::default_delete<int>> ] ... 完整错误信息详见编译示例
疑问
已知问题根源是std::unique_ptr不可拷贝,但不解为何仅在Windows平台添加__declspec(dllexport)时,才要求该类型具备可拷贝性?
代码库中__declspec(dllexport)是宏,Linux下会展开为__attribute__((visibility("default"))),此时代码可正常编译。
原因与解决方案
核心原因
MSVC处理__declspec(dllexport)标记的类时,会强制生成该类的完整导出元数据,包括对类的所有成员(这里是std::unordered_map)的拷贝构造/赋值操作的合法性检查——哪怕你的代码并没有显式调用这些操作。
std::unordered_map的内部实现依赖节点链表结构,MSVC生成导出代码时会触发对std::pair<const int*, std::unique_ptr<int>>的赋值操作检查。由于std::unique_ptr禁用了拷贝赋值,导致对应的std::pair拷贝赋值也不可用,最终触发编译错误。
为何Linux下无问题?
GCC/Clang处理__attribute__((visibility("default")))时,不会像MSVC那样强制检查类的所有隐式生成的特殊成员函数。Linux动态链接机制更宽松,只要代码中实际调用的操作合法,就不会触发这类检查——你的代码里并没有实际拷贝Foo或其内部的unordered_map,因此编译通过。
解决方案
- 移除不必要的导出标记:如果
Foo不需要跨DLL边界访问,直接去掉__declspec(dllexport)。 - 显式删除拷贝操作:在
Foo中显式删除拷贝构造和拷贝赋值,阻止MSVC尝试生成这些操作的导出代码:struct __declspec(dllexport) Foo { std::unordered_map<const int*, std::unique_ptr<int>> foo; // 显式删除拷贝操作 Foo(const Foo&) = delete; Foo& operator=(const Foo&) = delete; }; - 改用指针跨DLL传递:如果需要跨DLL暴露
Foo实例,导出返回std::unique_ptr<Foo>的工厂函数,而非直接导出Foo类的拷贝操作。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

