C++ DLL导出导致具有唯一指针的问题
我有两个文件: 标题.hC++ DLL导出导致具有唯一指针的问题,c++,stl,c++14,c++17,dllexport,C++,Stl,C++14,C++17,Dllexport,我有两个文件: 标题.h #pragma once #ifdef UNIQUEPTRISSUE_EXPORTS #define UNIQUEPTRISSUE_API __declspec(dllexport) #else #define UNIQUEPTRISSUE_API __declspec(dllimport) #endif UniquePtrIssue.cpp #include "stdafx.h" #include "Header.h" #include &l
#pragma once
#ifdef UNIQUEPTRISSUE_EXPORTS
#define UNIQUEPTRISSUE_API __declspec(dllexport)
#else
#define UNIQUEPTRISSUE_API __declspec(dllimport)
#endif
UniquePtrIssue.cpp
#include "stdafx.h"
#include "Header.h"
#include <memory>
#include <vector>
class UNIQUEPTRISSUE_API ClassA {
};
class UNIQUEPTRISSUE_API ClassB {
private:
std::vector<std::unique_ptr<ClassA>> x;
};
#包括“stdafx.h”
#包括“Header.h”
#包括
#包括
类UNIQUEPTRISSUE_API ClassA{
};
类UNIQUEPTRISSUE_API类B{
私人:
std::向量x;
};
编译会引发以下错误:
1> d:\program files(x86)\microsoft visual
studio\2017\enterprise\vc\tools\msvc\14.14.26428\include\xutility(2443):
错误C2280:'std::unique\u ptr>
&std::unique_ptr::operator=(常量)
std::unique_ptr&)':正在尝试
引用已删除的函数
1> 与
1> [
1> _Ty=A类
1> ]
当访问unique\u ptr
的复制构造函数时,似乎会出现类似的问题,但它们似乎不适用
从两个类声明中删除UNIQUEPTRISSUE\u API
/\uu declspec(dllexport)
,似乎可以消除错误
显然,
\uu declspec(dllexport)
声明中发生了一些我不理解的事情。有什么方法可以在导出的类之间使用unique\ptr
s吗?当您使用declspec(dllexport)
声明一个类时,编译器必须生成该类的所有成员函数,包括默认构造函数、复制赋值、,etc函数,因为它不知道导入模块可能需要哪些函数。这在中进行了描述
由于无法复制unique_ptr
,因此会删除其复制构造函数和复制赋值运算符,当向量对象尝试使用它们时,会出现C2280
错误
如果不包括declspec(dllexport)
,编译器将只生成实际使用的函数,并避免出现问题的副本
解决此问题的一种方法是导出单个类成员函数,这可能意味着将其中一些函数指定为默认函数<代码>虚拟函数不需要导出,因为它们由vtable处理
另一种解决方法是显式删除复制构造函数和复制赋值运算符。由于这将阻止创建默认构造函数和移动构造函数/赋值函数,因此可能需要在中对这些函数进行默认设置
class UNIQUEPTRISSUE_API ClassB {
public:
ClassB(const ClassB &) = delete;
ClassB &operator=(const ClassB &) = delete;
// You may need to explicitly default these if they are used
ClassB() = default;
ClassB &operator=(ClassB &&) = default;
ClassB(ClassB &&) = default;
private:
std::vector<std::unique_ptr<ClassA>> x;
};
class UNIQUEPTRISSUE\u API ClassB{
公众:
ClassB(常量ClassB&)=删除;
ClassB&运算符=(const ClassB&)=删除;
//如果使用它们,您可能需要显式地默认它们
ClassB()=默认值;
ClassB&运算符=(ClassB&&)=默认值;
ClassB(ClassB&&)=默认值;
私人:
std::向量x;
};
您可以以不同的方式公开类:
class ClassB {
private:
std::vector<std::unique_ptr<ClassA>> x;
public:
UNIQUEPTRISSUE_API ClassB(ClassB&&) {
}
UNIQUEPTRISSUE_API ClassB& operator==(ClassB&&) {
return* this;
}
private:
}
B类{
私人:
std::向量x;
公众:
UNIQUEPTRISSUE_API ClassB(ClassB&&){
}
UNIQUEPTRISSUE_API类B和运算符==(类B和){
归还*这个;
}
私人:
}
i、 e:不要导出整个类,而是导出单个函数。
我在vs2010和vs2017上尝试了这一点为什么首先要从DLL导出类?这是高度依赖于编译器的。即使您可以无错误地导出,您也无法在其他编译器中使用您的类,尤其是当它依赖于STL类时,因为DLL用户可能不会使用与DLL相同的STL实现。您应该尽量避免在DLL边界上使用非POD数据。感谢@RemyLebeau,我不太在意编译器依赖性,因为这是一个我只希望自己使用的个人项目。我之所以导出类,首先是因为我希望客户端能够对它们进行子类化。是否有其他方法可以在DLL边界上提供多态性和继承性?即使您自己使用它,总有一天您可能需要更新编译器,这需要重新编译DLL以匹配。多态性在DLL边界上并不能很好地工作,继承最好局限于仅对抽象接口公开访问的DLL(就像COM一样)。奇怪的是,我无法在上重现该错误……它很简洁,但不准确:
std::vector
可以很好地处理仅可移动的类型。我会认为MSVC的行为是一个错误。code>ClassB本身只能移动,因此没有理由生成复制赋值运算符并导致错误。@Quentin。这就是为什么我最初感到困惑的原因,因为我知道,尽管unique_ptr只能移动,但很可能有unique_ptr的向量。我在vs 2017中尝试了你的代码,它可以工作。您使用哪种编译器版本?@Quentin它准确地描述了编译器的行为,这是该语言的编译器扩展。显然,此行为尚未更新,无法解释正在导出的仅移动类。当将类声明为dllimport
时,编译器在尝试使用已删除函数时会给出一个错误,因此我没有理由知道无法修改导出行为。这很公平。我只是希望更多地强调这是一个编译器问题,因为std::vector
没有必要复制它不可复制的内容。