Why is my object not being copied when move constructor is deleted?

Deleted move members are evil. They are not outlawed, because some day, someone will find a clever use for them. But I haven’t seen a good use yet.

Deleting a special member is not the same thing as not having a special member. Nowhere is this more obvious than with the move constructor and move assignment operator.

When present, whether deleted, defaulted, or user-defined, the move constructor and the move assignment operator participate in overload resolution. That means that they “compete” with the special copy members. The copy members will typically favor const lvalues, whereas the move members attract rvalues.

When returning a local type from a function (when the local type is the same un-cv-qualified type as the return type), the return statement first considers the return expression as an rvalue, and only if it can’t find a suitable constructor, will it then be considered as an lvalue. I.e. matching the proper constructor for returning a local object from a function is a 2-phase operation.

When you don’t have a move constructor at all (not even deleted), but you do have a normal copy constructor (takes a const &), then the rvalue from the return statement will match the copy constructor.

When you do have a move constructor, even it is marked deleted, the rvalue from the return statement will find the move constructor a better match than the copy constructor.

Summary

Unless you really know what you are doing, never delete the move members. If you don’t want your type to be movable, just do not define the move members and make sure that you do declare copy members, even if the copy members are =default‘d.

Update

I suppose it’s hard to provide quotes from the Standard on what delete
doesn’t do? – DyP

8.4.3 Deleted definitions [dcl.fct.def.delete]

2 A program that refers to a deleted function implicitly or
explicitly, other than to declare it, is ill-formed. [ Note: This
includes calling the function implicitly or explicitly and forming a
pointer or pointer-to-member to the function. It applies even for
references in expressions that are not potentially-evaluated. If a
function is overloaded, it is referenced only if the function is
selected by overload resolution. — end note ]

Update 2

12.8 Copying and moving class objects [class.copy]

9 If the definition of a class X does not explicitly declare a move
constructor, one will be implicitly declared as defaulted if and only
if

  • X does not have a user-declared copy constructor,
  • X does not have a user-declared copy assignment operator,
  • X does not have a user-declared move assignment operator, and
  • X does not have a user-declared destructor.

[Note: When the move constructor is not implicitly declared or
explicitly supplied, expressions that otherwise would have invoked the
move constructor may instead invoke a copy constructor. — end note ]

Leave a Comment