Search code examples
c++visual-c++containersunique-ptrdllexport

dllexport a type with a std container of std::unique_ptr results in error C2280


I'm trying to dllexport a type with a std container of std::unique_ptr member, f.e.

struct __declspec(dllexport) C {
    std::vector<std::unique_ptr<int>> c_;
};

but whatever i try, msvc always complains about

msvc/14.33.31629/include\xutility(4145): error C2280: 'std::unique_ptr<int,std::default_delete<int>> &std::unique_ptr<int,std::default_delete<int>>::operator =(const std::unique_ptr<int,std::default_delete<int>> &)': attempting to reference a deleted function

The container type doesn't matter; the error is almost identical (always C2280) for vector, map, and others.

However, as soon as a std::unique_ptr member is added to the type, f.e.

struct __declspec(dllexport) C {
    std::vector<std::unique_ptr<int>> c_;
    std::unique_ptr<int> rup;
};

the error disappears.

I have no clue if this is the intended behavior, what causes this problem and how it can be fixed without adding a random std::unique_ptr member. What am i missing?

Demo


Solution

  • When you have a member std::vector<std::unique_ptr<int>> c_;, the compiler will provide a copy assignment operator C& operator=(const C&) that will try to copy the c_ member. This will fail to compile if it is actually used, since the elements of c_ can't be copied.

    __declspec(dllexport) seems to try to create this implicitly-declared function (maybe to export it? Though it doesn't seem to actually do that), which fails.

    When you add std::unique_ptr<int> rup;, this is a type without a copy constructor at all, so the implicit operator=(const C&) is suppressed (The move assign operator C& operator=(C&&) can still be used).

    You can get rid of the implicit copy-assign operator by declaring a move-assign operator or a move constructor:

    struct __declspec(dllexport) C {
        std::vector<std::unique_ptr<int>> c_;
        C(C&&) = default;
    };
    

    Or by inheriting from a move-only type/having a move-only member:

    struct move_only { constexpr move_only(move_only&&) noexcept = default; };
    
    struct __declspec(dllexport) C : private move_only {
        std::vector<std::unique_ptr<int>> c_;
    };