In C++98, the C++ compiler could automatically generate copy constructor and copy assignment operator via member-wise copy, e.g.
struct X {
std::string s;
std::vector<int> v;
int n;
};
The compiler automatically generates copy constructor and copy assignment operator for X, using member-wise copy.
But how do things change in C++11 with move semantics?
Are the move constructor and move assignment operator automatically generated, like copy constructors and copy assignment operators?
Are there cases in which move operations are not automatically generated?
Nikos Athanasiou gave a good answer but I wanted to add this tool that I think is very useful.
Here is a screenshot of Howard Hinnant's presentation "Everything You Ever Wanted To Know About Move Semantics (and then some)" from ACCU 2014 conference which I think is a very good reminder of the rules of automatic generation of special members:
Clarification from Mr Hinnant from the comments:
The slide doesn't say it, but the red squares indicate deprecated
behavior. I.e. if you don't want to depend upon deprecated behavior,
then declare both of your copy members if you declare your destructor,
or one of the copy members (basically follow the C++98/03 "rule of 3")
I recommend reading the slides to get the progressive construction of this table and have a detailed explanation of how and why we have this now.
Other presentations can be found there: http://accu.org/index.php/articles/1901
From the standard Ch. 12 - Special member functions
Par 12.8 Copying and moving class objects (emphasis mine)
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 ]
Then 11 explains the rules for deleting the defaulted move constructor
11 . An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/ move constructor for a class X is defined as deleted (8.4.3) if X has:
— a variant member with a non-trivial corresponding constructor and X is a union-like class,
— a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor, or,
— for the copy constructor, a non-static data member of rvalue reference type. A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4).
[ Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. —end note ]
On the complexity of it all *
The rules can be somewhat overwhelming. It's good to use some technique to bypass the complexity. Examples are :
Make use of the rule of zero to simplify the writing of the majority of your classes.
(On implicitly deleted) Explicitly default the special member function in question; if it would have been implicitly defined as deleted, the compiler will complain.
* points made in the comments by myself (1) and dyp (2)
Related
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
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 ]
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
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 ]
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
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 ]
Per this presentation, if either the copy constructor or copy assignment operator is "user declared", then no implicit move operations will be generated. Does deleteing the copy constructor or copy assignment operator count as "user declared"?
struct NoCopy {
NoCopy(NoCopy&) = delete;
NoCopy& operator=(const NoCopy&) = delete;
};
Will implicit move operations be generated for the NoCopy class? Or does deleting the relevant copy operations count as "user declared" and thus inhibit implicit move generation?
If possible, I'd prefer an answer referencing the relevant parts of the standard.
According to slide 14 of your presentation, a deleted copy constructor is "user declared" thus inhibiting the move generation.
The term "user declared" doesn't have a formal definition in the standard. It is meant to be the opposite of "implicitly declared" in the context of special member functions. [dcl.fct.def.default]/4 could be a bit clearer about this fact, but the intention is there:
Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (12.1 12.4, 12.8), which might mean defining them as deleted. A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed.
Both NoCopy(NoCopy&) = delete; and NoCopy& operator=(const NoCopy&) = delete; are declarations of special member functions. Since you are explicitly declaring them, as opposed to allowing the compiler to declare them implicitly, they are user-declared. Those declarations will therefore suppress the implicit declarations of the move constructor and move assignment operator per [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,
— X does not have a user-declared destructor, and
— the move constructor would not be implicitly defined as deleted.
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
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 ]