2

Is there something wrong if I write something like this:

Try<std::unique_ptr<int> > some_function() {
  std::unique_ptr<int> s(new int(2));
  return s;
}

Is the copy constructor invoked? Should I use std::move?

ibp73
  • 650
  • 1
  • 6
  • 16

2 Answers2

4

std::unique_ptr doesn't have a copy constructor. What you're doing there is the same as assigning with a unique_ptr: the pointer is moved. (though in some situations you have to explicitly move() the pointer or else you'll get a compilation error; but if the compiler doesn't complain with an error, then it's quietly moving the pointer)

Cornstalks
  • 37,137
  • 18
  • 79
  • 144
3

In a return statement, overload resolution can be performed as if the id-expression in the return statement designates an rvalue:

When the criteria for elision of a copy/move operation are met, [..], 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 [..], overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

So your case does indeed qualify for NRVO since s is declared in the body of the function, thus std::move() is not required as overload-resolution can treat s as an rvalue.

Note that std::move() might still be needed if your compiler doesn't support the first phase of overload resolution treating s as an rvalue when the type of the return expression doesn't have the same cv-unqualified type as the function's return type. This seems to be the case with the trunk version of clang but not gcc. More info in this thread.

Community
  • 1
  • 1
David G
  • 94,763
  • 41
  • 167
  • 253
  • 1
    Without the definition of `Try`, you cannot know that `std::move` is required here. Just because `std::unique_ptr` is not copyable, does not mean that `Try>` is not. – Benjamin Lindley Feb 15 '15 at 21:10
  • @BenjaminLindley You're right. The definition is needed. I will update. – David G Feb 15 '15 at 21:14
  • Even after using std::move, I am still getting an error while instantiating Try. It says I am trying to use a deleted function. – ibp73 Feb 15 '15 at 21:33
  • @ibp73 I need a minimal program that reproduces the error. – David G Feb 15 '15 at 21:34
  • The definition of try.hpp can be found here: https://github.com/3rdparty/stout/blob/master/include/stout/try.hpp – ibp73 Feb 15 '15 at 21:38
  • @ibp73 That error has nothing to do with how you are returning. In fact, you can only return an lvalue because your constructor `Try(T const&)` only takes lvalues. Then when you do `, t(new T(_t))` you call the copy-constructor of `std::unique_ptr` which you can't do. You also can't do `, t(new T(std::move(_t)))` because `_t` is `const` and you can't move from `const` objects. Why not just store a `T`, take `T` by value in the constructor and move it like this: `, t(std::move(_t))` – David G Feb 15 '15 at 21:44
  • @ibp73 Just a correction: Your can return both lvalues and rvalues but it won't matter if it's an rvalue because your constructor `Try(T const&)` will only store them as lvalue-references. Just wanted to clear that up. Does that hep? – David G Feb 15 '15 at 21:51
  • Your quote does not match your claim. `s` is "a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body", so "overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue." Whether it qualifies for copy elision is irrelevant. – T.C. Feb 16 '15 at 04:53
  • @T.C. But I'd figure it would have to qualify for copy-elision because it doesn't say anything about the type in the return expression has to match the return type of the function - when, [in this example](http://coliru.stacked-crooked.com/a/af8b655c28b629b9), that is how the rvalue constructor is chosen. When the return type is different the constructor taking an lvalue-reference is instead chosen. – David G Feb 16 '15 at 16:45
  • @0x499602D2 No, that's just compiler not being up to date with the standard. http://stackoverflow.com/questions/25875596/can-returning-a-local-variable-by-value-in-c11-14-result-in-the-return-value-b/25876175#25876175 – T.C. Feb 16 '15 at 16:48
  • @T.C. Ah, I see. That makes it so much clearer. So the OP shouldn't have to use `std::move()` but he might have to because his compiler might not support that optimization yet. – David G Feb 16 '15 at 16:55