如何在带有 Visual Studio 的 dll 导出类中使用唯一指针向量

Posted

技术标签:

【中文标题】如何在带有 Visual Studio 的 dll 导出类中使用唯一指针向量【英文标题】:How to use a vector of unique pointers in a dll exported class with Visual Studio 【发布时间】:2015-04-10 15:31:25 【问题描述】:

一个简单的例子

class __declspec(dllexport) A

public:
    vector<unique_ptr<int>> v;
;

unique_ptr 的已删除复制构造函数在 VS2013 编译中出错。如果我删除__declspec(dllexport),那很好。如果我只使用unique_ptr&lt;int&gt; v,也可以。这是编译器错误吗?有什么办法可以解决吗?谢谢。

您可以使用以下完整代码在http://webcompiler.cloudapp.net/ 上试用

#include <iostream>
#include <vector>
#include <memory>
using namespace std;

class __declspec(dllexport) A

public:
    vector<unique_ptr<int>> v;
;

int main()

   cout << "Hello World" << endl; 

产生编译器错误:

Compiled with /EHsc /nologo /W4 /c
main.cpp
main.cpp(9): warning C4251: 'A::v': class 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' needs to have dll-interface to be used by clients of class 'A'
        with
        [
            _Ty=int
        ]
c:\tools_root\cl\inc\xutility(2144): error C2280: 'std::unique_ptr<int,std::default_delete<_Ty>> &std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator =(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function
        with
        [
            _Ty=int
        ]
c:\tools_root\cl\inc\memory(1430): note: see declaration of 'std::unique_ptr<int,std::default_delete<_Ty>>::operator ='
        with
        [
            _Ty=int
        ]
c:\tools_root\cl\inc\xutility(2165): note: see reference to function template instantiation '_OutIt std::_Copy_impl<_InIt,_OutIt>(_InIt,_InIt,_OutIt,std::_Nonscalar_ptr_iterator_tag)' being compiled
        with
        [
            _OutIt=std::unique_ptr<int,std::default_delete<int>> *,
            _InIt=std::unique_ptr<int,std::default_delete<int>> *
        ]
c:\tools_root\cl\inc\vector(973): note: see reference to function template instantiation '_OutIt std::_Copy_impl<std::unique_ptr<int,std::default_delete<_Ty>>,std::unique_ptr<_Ty,std::default_delete<_Ty>>*>(_InIt,_InIt,_OutIt)' being compiled
        with
        [
            _OutIt=std::unique_ptr<int,std::default_delete<int>> *,
            _Ty=int,
            _InIt=std::unique_ptr<int,std::default_delete<int>> *
        ]
c:\tools_root\cl\inc\vector(956): note: while compiling class template member function 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>::operator =(const std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &)'
        with
        [
            _Ty=int
        ]
main.cpp(10): note: see reference to function template instantiation 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>::operator =(const std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &)' being compiled
        with
        [
            _Ty=int
        ]
main.cpp(9): note: see reference to class template instantiation 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' being compiled
        with
        [
            _Ty=int
        ]

【问题讨论】:

清除问题描述、编译器输出、MCVE - 如果所有 SO 问题都像这样! 如果我可以建议:不要这样做。您认为将此类放在 DLL 中而不是仅仅链接它的优势是什么?导出类时,DLL 的主要优点(特别是可重用性)不再适用。 我同意本的观点。如果您想获得 DLL 的好处,您只需要导出一个 C API(或者,请参阅经典的“沙漏接口”presented at CppCon 2014)。否则,只需使用静态链接(.lib / .a 文件)。在分发/重用 DLL 时处理 C++ ABI 问题是一场噩梦,相比之下它们的优势相形见绌。 @BenVoigt 如果这里没有显示 dll 的优势,最好不要这样做。目前,我有一个项目,它制作了一个供 GUI 使用的 dll 内核计算库(大多数是类)。使用静态库进行内核计算会更好吗?谢谢。 @user1899020:是的,静态链接可以很好地解决这个问题,并且不会出现与 DLL 相关的任何复杂问题。 【参考方案1】:

似乎添加__declspec(dllexport) 会强制编译器定义隐式声明的复制构造函数和复制赋值运算符(通常,只有在使用它们时才会发生这种情况)。它们依次调用v 的复制构造函数/赋值运算符。但是std::vector&lt;T&gt; 的复制操作对于不可复制的T 格式不正确,例如std::unique_ptr。因此出现错误。

当成员只是std::unique_ptr时,不会出现问题,因为复制操作被显式删除,所以A的默认复制操作也被删除。

因此,如果您明确删除复制操作,问题就解决了:

class __declspec(dllexport) A

public:
    A(const A&) = delete;
    A& operator=(const A&) = delete;
    vector<unique_ptr<int>> v;
;

当然,如果您想要复制功能,那么自己定义它们也会有所帮助。

【讨论】:

不是真的“错”,可以这么说。 vector&lt;unique_ptr&lt;int&gt;&gt;的拷贝构造函数没有被删除;它只会无法实例化。因此,A 的复制构造函数也不会被删除。复制分配同上。 @T.C.你当然是对的。我会改写答案。 A(A&amp;&amp;)=default; A&amp; operator=(A&amp;&amp;)=default; 会给你移动语义(如果你的编译器没有损坏,哦等等,nm),并且不需要delete 复制ctors。 由于要导出整个类,因此类的所有成员函数,无论是否使用,都需要实例化和导出,因为导入类的代码可能需要它们。

以上是关于如何在带有 Visual Studio 的 dll 导出类中使用唯一指针向量的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 C#、Visual Studio 2010 的 PhoneNumbers.dll 验证电话号码

带有 Visual Studio 2013 的 Qt 5.4 - 缺少 QtCored.dll

visual studio 2008如何引用dll

如何在 Visual Studio 2010 中使用 Visual Studio 2008 创建的 DLL?

Visual Studio .suo 和 .dll?

处理丢失 MSVCP140D.dll 的 Visual Studio 设置