Search code examples
c++c++17return-value-optimization

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


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

Solution

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