21

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.

6
  • 9
    From the documentation: "If the following condition is satisfied, the behavior is undefined: 1) T is not CopyInsertable into vector. 2) T is not MoveInsertable into vector." Similarly for emplace_back. All compilers are correct, since the code exhibits undefined behavior. Commented 2 days ago
  • Why do you want a copyable-but-not-movable type at all? Commented 2 days ago
  • 1
    three compilers compared side-by-side: godbolt.org/z/KsoWqjrc6
    – alfC
    Commented 2 days ago
  • 3
    It fails not only with Microsoft STL, but with libc++ as well, which prints clear error The specified type does not meet the requirements of Cpp17MoveInsertable: gcc.godbolt.org/z/8T6fbWe4e
    – Fedor
    Commented 2 days ago
  • 2
    Do you understand the practical cause of this - that pushing back may require resizing the container, and when you resize the container you want to move-construct the new buffer from the contents of the old? This branch gets compiled even though you only call it on an empty container, because what code is compiled is not determined by runtime state. Commented 2 days ago

3 Answers 3

19

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.

5
  • 3
    I wonder if there will ever be a legitimate use for a movable-forbiden but copyable class. (I agree that a movable-forbiden but copyable class is not a "value" type, and there is no need to support it from a container point of view.)
    – alfC
    Commented 2 days ago
  • 2
    @alfC No, because a move is a special kind of copy.
    – Caleth
    Commented 2 days ago
  • @Caleth, I don't disagree. That "move is a special type of copy" is a semantic interpretation of a language feature. I am saying that from the language point of view, moving and copying are allowed to do different things (and be deleted independently).
    – alfC
    Commented 2 days ago
  • @alfC just because it isn't forbidden, doesn't mean you should actively make a horrible mess
    – Caleth
    Commented yesterday
  • I know it is not the current convention (and not mine either), but arguably someone can claim that permitting a syntax like A a1(std::move(a2)); for a "heavy" object constructuion that cannot be optimized (made O(1)) would be misleading and he/she might want to delete the move constructor. Yes, it would not work with MSVC vector and many other things but they seem to be conventions mostly.
    – alfC
    Commented 21 hours ago
16

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.

0
11

The problem here is that explicitly deleted functions are still candidates for overload resolution, this is what essentially makes CopyInsertable requirements fail. Instead you may just define your copy operations explicitly and rely on the compiler to remove (i.e. not generate) the move operations, so they are no longer considered and the copy operations are used instead:

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

This, however, violates the rule of 5. Assuming the reason you had to delete the move operations explicitly was some non-standard resource management, I'd recommend encapsulating this part in a member of the class instead and follow the rule of 0 and then let the compiler decide which operations it should synthesise based on your member classes.

3
  • This would violate the rule of 5. I would rather implement the move i think. Commented 2 days ago
  • @FantasticMrFox i'd rather follow the rule of 0 and encapsulate any non-standard resource management in the class members. The compiler may then decide whether it can generate move/copy operations based on how the members are made Commented 2 days ago
  • Good point, you should reformat your answer for that i think. Commented 2 days ago

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.