Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning move-only type compiles even though copy-constructor is unavailable

Tags:

c++

c++14

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?

like image 618
Andrew Tomazos Avatar asked Feb 09 '15 13:02

Andrew Tomazos


People also ask

Are move constructor automatically generated?

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.

What is the use of move constructor in C++?

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.

Does compiler provide default move constructor?

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.

What is Move semantics in C++?

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.


1 Answers

I thought that the return value of f() was copy-initialized by x, but std::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 a return 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. [...]

like image 56
Andy Prowl Avatar answered Nov 15 '22 16:11

Andy Prowl