34

I'm trying to use this code to demonstrate the use of the copy-constructor. My presumption was that when I have a function that returns by value, my compiler will, by default, perform a move of the object. But when the move-constructor is unavailable, the compiler will copy instead (in C++03, the compiler would copy when returning by-value). So why in the following example does the compiler try to call the explicitly-deleted move-constructor instead of the available copy-constructor? I'm compiling this in GCC 4.7.2.

struct S
{
    S() = default;
    S(S const &) = default;
    S(S&&) = delete;
};

S f() { return S{}; }

int main()
{
    f();
}

prog.cpp: In function ‘S f()’:
prog.cpp:8:18: error: use of deleted function ‘S::S(S&&)’
prog.cpp:5:5: error: declared here

Me myself and I
  • 3,990
  • 1
  • 23
  • 47
  • I'd say deleted functions participate in overload resolution, like inaccessible functions. – dyp May 23 '13 at 00:46
  • @DyP: I'm pretty sure they're not supposed to, wouldn't that completely defeat the point of using `=delete`? – Mooing Duck May 23 '13 at 00:49
  • @MooingDuck I don't think so, if `delete` is meant to prevent certain functions from getting called. Replacing the calls by other calls might lead to subtle, unintended changes. – dyp May 23 '13 at 00:50
  • @DyP: I'm pretty sure the _intent_ behind `=delete` is to tell the compiler to definitely use those other calls instead of this function. – Mooing Duck May 23 '13 at 00:52
  • You can make it even simpler: `S s{ S{} };` -> same problem – dyp May 23 '13 at 00:53
  • 1
    @MooingDuck `S(S&& p) : S((S const&)p) {}` – dyp May 23 '13 at 00:55
  • 2
    Related question: http://stackoverflow.com/questions/14085620/why-do-c11-deleted-functions-participate-in-overload-resolution – Timo May 23 '13 at 00:56
  • 3
    @MooingDuck: You are wrong. The intent on `= delete` is to provide a declaration that will cause an error if it is selected as the best overload. It is the only way of actually implementing, well, what the OP does not want: a function that can never be called with a temporary. – David Rodríguez - dribeas May 23 '13 at 04:09
  • We've laughed a lot within our team on that! =)) Ok, stop. Do you really want to make this code compile? There are several approaches to help compiler understand our intentions: 1) signature of the move ctor is like yours - x-reference to non-const object; a) just mark variable as const - `const S s; return s;`; b) use `const_cast(s)`; 2) static_cast - static_cast(s). – gshep May 19 '16 at 19:45

1 Answers1

43

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 ]

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 2
    I suppose it's hard to provide quotes from the Standard on what `delete` doesn't do? – dyp May 23 '13 at 00:58