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?

4
  • 5
    It actually uses the std::unique_ptr<>'s move constructor. Commented Sep 17, 2014 at 7:56
  • 4
    Please see this related question. And whoever closed this as a dupe of "undefined behavior" needs a coffee. Commented Sep 17, 2014 at 7:56
  • 3
    This is called copy elision Commented Sep 17, 2014 at 7:57
  • 2
    @MattMcNabb There still has to be a viable copy/move copy constructor overload. Commented Sep 17, 2014 at 8:02

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

Comments

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!!!
}

8 Comments

This isn't generally true. It only applies in the situation where p qualifies for copy elision.
@MattMcNabb: Which is on every return expression with value. And a few other places.
@rodrigo No, it isn't that simple. The conditions for copy elision are more complicated than that (unfortunately!)
@rodrigo " 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" .
i.e your answer is still wrong.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.