C++ DLL导出导致具有唯一指针的问题

C++ 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

我有两个文件:

标题.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 <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
没有必要复制它不可复制的内容。