How can a unique_ptr be returned by value without std::move? [duplicate]
Asked Answered
S

2

12
std::unique_ptr<int> ptr() {
    std::unique_ptr<int> p(new int(3));
    return p;  //  Why doesn't this require explicit move using std::move?
}  // Why didn't the data pointed to by 'p' is not destroyed here though p is not moved?

int main() {
    std::unique_ptr<int> a = ptr();  // Why doesn't this require std::move? 
    std::cout << *a; // Prints 3.
}

In the above code, the function ptr() returns a copy of p. When p goes out of scope, the data '3' should get deleted. But how does the code work without any access violation?

Stick answered 17/9, 2014 at 7:49 Comment(4)
It actually uses the std::unique_ptr<>'s move constructor.Stanwin
Please see this related question. And whoever closed this as a dupe of "undefined behavior" needs a coffee.Judson
This is called copy elisionTrypsin
@MattMcNabb There still has to be a viable copy/move copy constructor overload.Zoogloea
Z
16

This is set out in the C++11 standard, § 12.8/32:

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....

(emphasis mine). In plain english, it means that the lvalue p can be treated as an rvalue when it comes to overload resolution, because it is a candidate for copy elision. This in turn means the move constructor is picked up on overload resolution (in actual fact, the move copy is probably elided anyway.)

Zoogloea answered 17/9, 2014 at 7:58 Comment(0)
T
6

Because return of certain expressions, such as local automatic variables, are explicitly defined to return a moved object, if the moving operator is available.

So:

return p;

is more or less similar to:

return std::move(p);

But note that this will not work for example with a global variable.

std::unique_ptr<int> g(new int(3));
std::unique_ptr<int> ptr() {
    return g;  //  error!!!
}
Trinee answered 17/9, 2014 at 7:56 Comment(8)
This isn't generally true. It only applies in the situation where p qualifies for copy elision.Trypsin
@MattMcNabb: Which is on every return expression with value. And a few other places.Trinee
@Trinee No, it isn't that simple. The conditions for copy elision are more complicated than that (unfortunately!)Zoogloea
@Trinee " in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value" .Trypsin
@MattMcNabb & juanchopanza: Oh! You both are right! A return of a global variable will not qualify for copy elision, for example.Trinee
@Trinee Other examples: a function parameter, or something inside an if statement.Zoogloea
i.e your answer is still wrong.Zoogloea
@juanchopanza: Fair enough. I've rephrased it. In this answer I prefer to avoid excessive technical terms or too much details.Trinee

© 2022 - 2024 — McMap. All rights reserved.