简体   繁体   中英

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

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. ap 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 ap . It's not inherently safe to move from it, so the language won't try to do it automatically.

Copy elision can't apply (among other reasons) since ap is a std::unique_ptr , which is uncopyable. And since ap has a lifetime beyond the body of A::Foo(A&) , it would be very surprising (as in, surprising to the person writing the code) if the compiler automatically tried to move from ap , which would likely wreck the class invariants of a . It would work if you return std::move(ap); , but that explicitly steals ap .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM