Why does returning a std::optional sometimes move and sometimes copy?

  • A+

See the below example of returning an optional of UserName - a movable/copyable class.

std::optional<UserName> CreateUser() {    UserName u;    return {u}; // this one will cause a copy of UserName    return u;   // this one moves UserName }   int main() {    auto d = CreateUser(); } 

Why does return {u} cause a copy and return u a move?

Here's the related coliru sample: http://coliru.stacked-crooked.com/a/6bf853750b38d110

Another case (thanks to the comment from @Slava):

std::unique_ptr<int> foo()  {      std::unique_ptr<int> p;      return {p};  // uses copy of unique_ptr and so it breaks... } 


Because returning a name of an object with automatic storage duration is treated as returning an rvalue of the object. Note this works only if the expression in the return statement is a (possibly parenthesized, not including braces) name, like return u; or return (u);, so return {u}; works as usual, i.e. the copy constructor is invoked.

Related part in the standard [class.copy.elision]/3:

In the following copy-initialization contexts, a move operation might be used instead of a copy operation:

  • If the expression in a return statement ([stmt.return]) 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, or
  • ...

overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: