Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DLL exporting causing issues with unique pointers

I've got two files:

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

Compiling raises the following error:

1>d:\program files (x86)\microsoft visual studio\2017\enterprise\vc\tools\msvc\14.14.26428\include\xutility(2443): error C2280: 'std::unique_ptr> &std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator =(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function 1> with 1> [ 1> _Ty=ClassA 1> ]

Similar issues seem to arise when the accessing the copy constructor of a unique_ptr but they don't seem to apply.

Removing the UNIQUEPTRISSUE_API/__declspec(dllexport) from both class declarations seems to make the error go away.

Obviously something is going on with the __declspec(dllexport) declaration that I don't understand. Is there any way I can use unique_ptrs between exported classes?

like image 519
Alistair401 Avatar asked Mar 06 '23 22:03

Alistair401


1 Answers

When you declare a class with declspec(dllexport), the compiler must generate all of the member functions of the class, including the default constructors, copy assignment, etc functions since it doesn't know which ones may be needed by the importing module. This is described in Using dllimport and dllexport in C++ classes.

Since a unique_ptr cannot be copied, its copy constructor and copy assignment operators are deleted, and when the vector object tries to use them you get the C2280 error.

When you don't include declspec(dllexport), the compiler will only generate the functions that are actually used, and the problematic copies are avoided.

One way around this problem is to export the individual class member functions, which may mean specifying some of them as defaulted. virtual functions would not need to be exported, since they're handled by the vtable.

Another workaround is to explicitly delete the copy constructor and copy assignment operator. Since this will prevent the creation of a default constructor and move constructor/assignment functions, you may need to default those in.

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;
};
like image 63
1201ProgramAlarm Avatar answered Mar 15 '23 23:03

1201ProgramAlarm