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 ]
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 ]
As far as I know, thrown objects are copied by default. So the copy constructor should be called when I throw an object. I also know that the compiler may optimize and elide the copying. I have a simple program here where I'm throwing a class called X. It's expected that if I make the class non-copyable, the object can't be thrown. But something unexpected happens. If I delete the copy constructor, the compiler complains about that. If I comment the deletion of the copy constructor and delete the move constructor, the complier complains about deleting the move constructor.
the code:
class X
{
public:
int code;
X(int code) : code(code) {}
//X(X&) = delete;
//X(X&&) = delete;
};
void func()
{
X x(4);
throw x;
}
int main() { }
The error when I delete the copy constructor:
main.cpp: In function ‘void func()’:
main.cpp:13:11: error: use of deleted function ‘X::X(X&)’
throw x;
^
The error when I delete the move constructor:
main.cpp: In function ‘void func()’:
main.cpp:13:11: error: use of deleted function ‘X::X(X&&)’
throw x;
^
Can someone explain why this is happening?
Edit: If I delete the copy constructor and provide an implementation for the move constructor, the code works fine. Whereas if I delete the move constructor and provide an Implementation for the copy constructor, I still get the same error. Why can't I delete the move constructor?
code:
class X
{
public:
int code;
X(int code) : code(code) {}
X(X&) = default;
X(X&&) = delete;
};
void func()
{
X x(5);
throw x;
}
int main() { }
error:
main.cpp: In function ‘void func()’:
main.cpp:13:11: error: use of deleted function ‘X::X(X&&)’
throw x;
^
The initialization of the exception object follows the same rules as the initialization of other objects: when the initializer is an lvalue, the copy constructor is used, and when the initializer is an rvalue, the move constructor is used. However, see C++17 [class.copy.elision]/3:
... if the operand of a throw-expression (8.17) is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), overload resolution to select the constructor for the copy is first performed as if the object were designated
by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. — end note ]
Here, you are throwing the object in a context where it is treated as an rvalue first. If overload resolution fails, it will then be treated as an lvalue. Therefore, in general, the compiler prefers to use the move constructor.
When the copy constructor is explicitly deleted, the compiler does not generate the move constructor ([class.copy.ctor]/8), so the code doesn't compile.
When the move constructor is explicitly deleted, the first stage of overload resolution selects the move constructor. An error occurs because the function selected by overload resolution is deleted. (Note: a defaulted move constructor that is defined as deleted is ignored by overload resolution ([class.copy.ctor]/10). An explicitly deleted move constructor is not ignored by overload resolution.)
When both the copy and move constructors are explicitly deleted, the previous paragraph still holds.
Quoting from cppreference
Even if copy initialization selects the move constructor, copy initialization from lvalue must be well-formed, and the destructor must be accessible (since C++14)
And
If the type of expression is a class type, its copy/move constructor and destructor must be accessible even if copy elision takes place.
Apparently, for well defined exception class a copy constructor is necessary, even if it wouldn't be used. C++11 standard didn't mandate that, but it was fixed in C++14.
I'm not 100% sure how to interpret "This may call the move constructor for rvalue expression" - whether move constructor is also required or not. For backwards compatibility it shouldn't be, but perhaps someone can quote standard on that.
When throwing an object, is it copied or moved?
The thrown object will be copy-initialised, and the lvalue local object will be treated as an rvalue - same as if returning a local lvalue.
Thus, if the type has a move constructor, then it will be moved in your example. If the class is only copyable but not movable, then it will be copied. Likewise, if the throw expression is not (treated as) an rvalue, then it is copied.
If the type is neither copyable nor movable, then it cannot be thrown. The copy or move may be elided in some cases but that has no effect on whether the type has to be copyable or movable.
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 ]
As per the C++ standard 12.8.7:
If the class definition declares a move constructor or move
assignment operator, the implicitly declared copy constructor is
defined as deleted;
and 12.8.18
If the class definition declares a move constructor or move assignment
operator, the implicitly declared copy assignment operator is defined
as deleted;
I am wondering why the move constructor/move assignment are not implicitly declared and defined as deleted (c++11 standard will not generate implicitly declared move constructor/move assignment in this case), if we only defined copy constructor or copy assignment operator?
If this was the case, then using an rvalue as the source of construction or assginment would result in a compilation error instead of falling back to a copy.
A function which does not exist (obviously) does not participate in overload resolution. A function which is defined as deleted does participate in overload resolution normally; if it's chosen, the compilation results in an error.
This code compiles:
struct Normal
{
Normal() {}
Normal(const Normal &) {}
};
int main()
{
Normal n(Normal{});
}
While this code results in an error:
struct Deleted
{
Deleted() {}
Deleted(const Deleted &) {}
Deleted(Deleted&&) = delete;
};
int main()
{
Deleted d(Deleted{});
}
If the move constructor were deleted in that case, then trying to copy-initialise from an rvalue would be an error - the deleted move constructor would be a better match than the copy constructor.
Usually, you'd want copy-initialisation to copy, rather than be disallowed, if you haven't defined move semantics. To give that behaviour, the move constructor is simply not declared at all, so that copy-initialisation uses the copy constructor whether copying from an lvalue or an rvalue. (As long as the copy constructor takes its argument by const reference.)
You can still delete the move operations yourself, if for some reason you want the rather odd quality of only being copyable from an lvalue.
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 ]