Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Only copiable type not accepted in msvc std::vector implementation

In the following code:

struct copy_only
{
    copy_only() = default;
    copy_only(const copy_only&)            = default;
    copy_only& operator=(const copy_only&) = default;
    copy_only(copy_only&&)                 = delete;
    copy_only& operator=(copy_only&&)      = delete;
    ~copy_only()                           = default;
};


std::vector<copy_only> v;
copy_only c{};
v.push_back(c);

On MSVC we get the error:

error C2280: 'copy_only::copy_only(copy_only &&)': attempting to reference a deleted function

This comes from within the vector implementation where push_back(const&) calls emplace_back implementation:

note: while compiling class template member function 'void std::vector<copy_only,std::allocator<copy_only>>::push_back(const _Ty &)'

note: see reference to function template instantiation '_Ty &std::vector<_Ty,std::allocator<_Ty>>::_Emplace_one_at_back<const _Ty&>(const _Ty &)' being compiled

This compiles with gcc and clang. Is this just a massive MSVC compiler bug on the simplest vector example? Or is there some standard thing that would prevent this usage that gcc and clang are just glossing over?

Live example.

like image 395
Fantastic Mr Fox Avatar asked Sep 09 '25 18:09

Fantastic Mr Fox


2 Answers

TL;DR: This is a poorly written class.

Copyable classes shouldn't actively resist moving, even if they don't want to have any custom move behavior.

Instead they should let moving fall back to copying, which happens automatically if you don't write move operations at all, instead of explicitly deleting them.


This isn't just something that doesn't play nice with std::vector. This will constantly cause issues, because nobody expects classes to do this.

like image 110
HolyBlackCat Avatar answered Sep 12 '25 17:09

HolyBlackCat


vector::push_back() has a Precondition of "T is Cpp17CopyInsertable into X", and Cpp17CopyInsertable implies Cpp17MoveInsertable according to [container.alloc.reqmts-2.4]:

T is Cpp17CopyInsertable into X means that, in addition to T being Cpp17MoveInsertable into X, the following expression is well-formed:

 allocator_traits<A>::construct(m, p, v)

and its evaluation causes the following postcondition to hold: The value of v is unchanged and is equivalent to *p.

Since your class is not Cpp17MoveInsertable and therefore not Cpp17CopyInsertable, which violates the Precondition of push_back() and leads into undefined behavior.

like image 31
康桓瑋 Avatar answered Sep 12 '25 17:09

康桓瑋