导出包含不可拷贝对象向量的类时出现编译错误的问题咨询
嘿,这个问题我之前在VS里也踩过坑,来给你掰扯清楚来龙去脉,还有解决办法~
首先先对齐下你的问题场景:你定义了一个禁止拷贝的NonCopiable类,当Exported类只包含单个NonCopiable成员时,加__declspec(dllexport)导出完全没问题,但换成std::vector<NonCopiable>就编译报错,提示引用了被删除的拷贝赋值运算符,对吧?
为啥导出类时会触发这个问题?
首先得搞懂__declspec(dllexport)在VC++里的作用:当你导出一个类时,编译器需要生成类的导出表,这个过程中会对类的特殊成员函数(拷贝构造、拷贝赋值这些)做额外的检查和代码生成准备。
单个NonCopiable成员为啥没问题?
当Exported只有单个NonCopiable成员时,Exported的拷贝构造和拷贝赋值运算符会被隐式删除——因为它的成员没法拷贝。这时候VC++编译器很聪明,它能直接识别出这些特殊函数是被删除的,所以不会尝试去生成它们的导出代码,自然也就不会触发错误。
换成vector就炸锅的原因
std::vector是个模板容器,它的很多操作(比如拷贝赋值)内部会依赖元素的拷贝能力。关键在于:当你导出Exported类时,VC++会尝试实例化vector的部分模板代码来生成导出相关的信息——哪怕你根本不会用到Exported的拷贝操作。
在这个实例化过程中,编译器会检查vector的拷贝赋值逻辑,而这个逻辑里会间接引用到NonCopiable的拷贝赋值运算符(哪怕实际上不会被执行)。但因为NonCopiable的拷贝赋值已经被你显式删除了,编译器就会抛出那个错误。
简单说就是:单个成员的情况,编译器一眼就看出类的拷贝操作不可行,直接跳过;但vector是模板,编译器得钻进去看容器的内部逻辑,结果就撞到了被删除的函数上。
解决方案:显式删除Exported的拷贝操作
最直接有效的办法,就是把Exported的拷贝构造和拷贝赋值运算符显式删除,让编译器彻底放弃为它们生成任何代码(包括导出表条目),代码改成这样:
class NonCopiable { public: NonCopiable() = default; NonCopiable(const NonCopiable&) = delete; NonCopiable& operator=(const NonCopiable&) = delete; NonCopiable(NonCopiable&&) noexcept = default; NonCopiable& operator=(NonCopiable&&) noexcept = default; }; class __declspec(dllexport) Exported { public: Exported() = default; // 显式删除拷贝构造和拷贝赋值 Exported(const Exported&) = delete; Exported& operator=(const Exported&) = delete; // 移动操作可以保留默认生成(如果需要的话) Exported(Exported&&) noexcept = default; Exported& operator=(Exported&&) noexcept = default; std::vector<NonCopiable> m_vector; };
这样做之后,编译器明确知道Exported的拷贝操作是禁用的,就不会再去实例化那些会触发错误的vector相关模板代码,导出也就正常了。而且这也符合你设计NonCopiable的初衷——既然元素都不能拷贝,那包含这些元素的容器类自然也不应该被拷贝,显式删除完全合理。
内容来源于stack exchange

