DLL导出导致唯一指针出现问题

时间:2018-06-26 00:16:30

标签: c++ stl c++14 c++17 dllexport

我有两个文件:

Header.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;
};

编译会引发以下错误:

  

1> d:\ program files(x86)\ microsoft visual   studio \ 2017 \企业\ vc \ tools \ msvc \ 14.14.26428 \ include \ xutility(2443):   错误C2280:“ std :: unique_ptr>   &std :: unique_ptr <_Ty,std :: default_delete <_Ty >> :: operator =(const   std :: unique_ptr <_Ty,std :: default_delete <_Ty >>&)':尝试   引用已删除的功能       1>与       1> [       1> _Ty = ClassA       1>]

相似的问题似乎在访问unique_ptr的副本构造函数时出现,但似乎并不适用。

从两个类声明中删除UNIQUEPTRISSUE_API / __declspec(dllexport)似乎会使错误消失。

显然,__declspec(dllexport)声明正在发生某些我不理解的事情。有什么方法可以在导出的类之间使用unique_ptr

2 个答案:

答案 0 :(得分:2)

当您使用declspec(dllexport)声明一个类时,编译器必须生成该类的所有成员函数,包括默认构造函数,副本分配等函数,因为它不知道可能需要哪些成员函数。导入模块。这在Using dllimport and dllexport in C++ classes中有描述。

由于无法复制unique_ptr,因此将删除其复制构造函数和复制赋值运算符,并且当vector对象尝试使用它们时,会出现C2280错误。

当您不包括declspec(dllexport)时,编译器将仅生成实际使用的函数,从而避免了有问题的副本。

解决此问题的一种方法是导出单个类成员函数,这可能意味着将其中一些指定为默认值。 virtual函数无需导出,因为它们由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;
};

答案 1 :(得分:1)

您可以不同地公开类:

class ClassB {
private:
    std::vector<std::unique_ptr<ClassA>> x;

public:

    UNIQUEPTRISSUE_API ClassB(ClassB&&) {

    }

    UNIQUEPTRISSUE_API ClassB& operator==(ClassB&&) {

        return* this;

    }

private:

}

即:不导出整个类,而是导出单个函数。 我在vs2010和vs2017上尝试过