The following compiles without error:
#include <memory>
std::unique_ptr<int> f() {
std::unique_ptr<int> x(new int(42));
return x;
}
int main() {
std::unique_ptr<int> y = f();
}
I thought that the return value of f()
was copy-initialized by x
, but std::unique_ptr
is a move-only type. How is it that this isn't ill-formed because the copy constructor isn't available? What is the relevant clause in the standard? Is there somewhere that says if f()
is a move-only type than a return statement becomes a move construction instead of a copy construction?
If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.
A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. For more information about move semantics, see Rvalue Reference Declarator: &&. This topic builds upon the following C++ class, MemoryBlock , which manages a memory buffer.
In C++, the compiler creates a default constructor if we don't define our own constructor. In C++, compiler created default constructor has an empty body, i.e., it doesn't assign default values to data members. However, in Java default constructors assign default values.
Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another.
I thought that the return value of
f()
was copy-initialized byx
, butstd::unique_ptr
is a move-only type
The return value of f()
is indeed copy-initialized from the expression x
, but copy-initialization does not always imply copy-construction. If the expression is an rvalue, then the move constructor will be picked by overload resolution (assuming a move constructor is present).
Now although it is true that the expression x
in the return x;
statement is an lvalue (which may lead you to think that what I just wrote does not apply), in situations where a named object with automatic storage duration is returned, the compiler shall first try to treat the id-expression as an rvalue for overload resolution.
What is the relevant clause in the standard? Is there somewhere that says if
f()
is a move-only type than areturn
statement becomes a move construction instead of a copy construction?
Per paragraph 12.8/32 of the C++ Standard ([class.copy]/32, draft N4296):
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a
return
statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. [...]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With