Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning member unique_ptr from class method

I am trying to return a std::unique_ptr class member (trying to move the ownership) to the caller. The following is a sample code snippet:

class A {
public:
  A() : p {new int{10}} {}

  static std::unique_ptr<int> Foo(A &a) {
    return a.p; // ERROR: Copy constructor getting invoked
                // return std::move(a.p); WORKS FINE
  }

  std::unique_ptr<int> p;
};

I thought the compiler (gcc-5.2.1) would be able to do return value optimization (copy elision) in this case without requiring the explicit intent via std::move(). But that isn't the case. Why not?

The following code seems to be working fine, which seems equivalent:

std::unique_ptr<int> foo() {
  std::unique_ptr<int> p {new int{10}};
  return p;
}
like image 795
axg Avatar asked Sep 30 '16 22:09

axg


1 Answers

The rule in [class.copy] is:

[...] 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 or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

In this example:

std::unique_ptr<int> foo() {
  std::unique_ptr<int> p {new int{10}};
  return p;
}

p is the name of an object with automatic storage duration declared in the body of the function. So rather than copying it into the return value, we first try to move it. That works fine.

But in this example:

static std::unique_ptr<int> Foo(A &a) {
    return a.p;
}

that doesn't apply. a.p isn't the name of an object at all, so we don't try overload resolution as if it were an rvalue, we instead just do the normal thing: try to copy it. This fails, so you have to explicitly move() it.


That's the wording of the rule, but it might not answer your question. Why is this the rule? Basically - we're trying to be safe. If we're naming a local variable, it's always safe to move from it in a return statement. It will never be accessed again. Easy optimization, no possible downside. But in your original example, a isn't owned by this function, neither is a.p. It's not inherently safe to move from it, so the language won't try to do it automatically.

like image 138
Barry Avatar answered Nov 08 '22 07:11

Barry