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.
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 ]
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 compile the following simple code with GCC 4.7.2 (MinGW). Here I'm using C++11 feature - non-static member initializers:
#include <iostream>
using namespace std;
struct A
{
int var;
A()
{
cout << "A()\n";
}
A(int i)
{
cout << "A(int i)\n";
var = i;
}
A(const A&) = delete;
};
struct B
{
A a = 7;
};
int main()
{
B b;
cout << "b.a.var = " << b.a.var;
return 0;
}
This code doesn't compile because of deleted copy-constructor which isn't necessary here. Here are errors:
main.cpp:27:11: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here
main.cpp: In constructor 'constexpr B::B()':
main.cpp:25:8: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here
If I implement copy-constructor like this:
A(const A& a)
{
cout << "A(const A&)\n";
var = a.var;
}
Then code compiles fine and program gives me expected output:
A(int i)
b.a.var = 7
So it means that copy constructor is not used, but why I can't delete it?
Edit: Thanks for your answers. Copy or move constructor is required by standard if I'm using =. To fix this problem I need to implement move constructor or use direct initialization syntax A a{7}.
The initialiser for a gives you copy-initialization:
A a = 7;
For such a copy-initialization, where a user-defined conversion is required, the resulting initialisation is equivalent to:
A a(A(7));
That is, a temporary A is constructed and then passed to the copy constructor of your a object. This copying may be elided, but the copy constructor must be available nonetheless. In other words, the copying can only be elided if the copy would be possible in the first place. If you delete the copy constructor, the copying is impossible.
You'll have a better time with your deleted copy constructor if you do the following:
A a{7};
This does direct-initialization and no copy constructor is required.
Copy initialization is allowed to elide the copy but the copy constructor is mandated to be accessible by the standard.
Per Paragraph 12.2/14 of the C++11 Standard:
The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization. [ Note: Copy-initialization may invoke a move (12.8). —end note ]
The reason why your copy-initialization doesn't compile is that during copy-initialization a temporary object needs to be created (at least logically), and the object being initialized shall be constructed from it.
Now all the previous answers seem to focus just on copy-constructors, but the first problem here is the absence of a move-constructor. As long as you provide one, then it is true that the copy constructor is not necessary.
Alas, deleting the copy constructor prevents the generation of an implicit move constructor. Adding one explicitly would fix the problem:
struct A
{
int var;
A()
{
cout << "A()\n";
}
A(int i)
{
cout << "A(int i)\n";
var = i;
}
A(const A&) = delete;
// THIS MAKES IT WORK
A(A&& a)
{
cout << "A(A&&)\n`;
var = a.var;
}
};
Notice that when both the move-constructor and the copy-constructor are present, the move-constructor is preferred, because the temporary created for copy-initializing your object is an rvalue.
When a move-constructor is absent, the compiler can invoke the copy constructor to perform the initialization, because constant lvalue references can bind to rvalue references and copy is seen as an unoptimized move.
However, even though the compiler is allowed to elide the call to the move or the copy constructor, the semantics of the operation must still be checked. Per Paragraph 12.8/32 of the C++11 Standard:
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, 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 ] [...]
Therefore, an error is issued by the compiler if neither the move constructor nor the copy constructor are present.
If you want, however, you can direct-initialize your object rather than copy-initializing it. Just use the direct-initialization syntax instead:
struct B
{
A a{7};
};
This will make the move-constructor and copy-constructor unnecessary, because no temporary is created when direct-initializing an object.
This code doesn't compiles because of deleted copy-constructor which isn't necessary here
Sorry, but your copy constructor is necessary. Even though the copy can be optimised out, it must still be possible in the code. This mandated by the language.
So it means that copy constructor is not used, but whyI can't delete it?
In your case, copy constructor is used only for semantic-check as required by the Standard, it also needs to be accessible. Later on, the compiler optimizes the code, eliding the call to the copy-constructor, so it is not actually invoked.