从 class 方法返回成员 unique_ptr

Returning member unique_ptr from class method

我正在尝试 return 一个 std::unique_ptr class 成员(试图转移所有权)给调用者。以下是示例代码片段:

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

我认为编译器 (gcc-5.2.1) 在这种情况下能够进行 return 值优化(复制省略)而不需要通过 std::move() 的明确意图。但事实并非如此。为什么不呢?

下面的代码似乎工作正常,这似乎是等价的:

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

[class.copy]中的规则是:

[...] 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.

在这个例子中:

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

p 是在函数体中声明的具有自动存储期限的对象的名称。因此,我们不是将其复制到 return 值中,而是首先尝试移动它。效果不错。

但在这个例子中:

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

这不适用。 a.p 根本不是一个对象的名字,所以我们不会像它是一个右值那样尝试重载决议,我们只是做正常的事情:尝试复制它。这失败了,所以你必须明确地move()它。


这是规则的措辞,但它可能无法回答您的问题。为什么这是规则?基本上 - 我们正在努力确保安全。如果我们正在命名一个局部变量,在 return 语句中从它移动总是安全的。它永远不会被再次访问。易于优化,没有可能的缺点。但是在您的原始示例中,a 不属于此函数,a.p 也不属于该函数。离开它本身并不安全,因此该语言不会尝试自动执行此操作。

不能应用复制省略(以及其他原因),因为 a.p 是一个 std::unique_ptr,它是不可复制的。并且由于 a.p 的生命周期超出了 A::Foo(A&),如果编译器自动尝试从 a.p ,这可能会破坏 a 的 class 不变量。如果你 return std::move(a.p);,它会起作用,但那会明确窃取 a.p