13

我正在尝试将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)在这种情况下能够进行返回值优化(复制省略),而不需要通过std::move(). 但事实并非如此。为什么不?

以下代码似乎工作正常,这似乎等效:

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

2 回答 2

11

[class.copy]中的规则是:

[...] 当语句中的表达式return一个(可能带括号的)id 表达式,它命名一个对象,该对象具有在最内层封闭函数或lambda 表达式的主体或参数声明子句中声明的自动存储持续时间,重载决议首先执行为副本选择构造函数,就好像对象是由右值指定的一样。

在这个例子中:

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

p是在函数体中声明的具有自动存储持续时间的对象的名称。因此,我们首先尝试移动它,而不是将其复制到返回值中。这很好用。

但在这个例子中:

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

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


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

于 2016-09-30T23:16:47.990 回答
-1

复制省略不能应用(除其他原因外),因为a.p是 a std::unique_ptr,这是不可复制的。并且由于a.p在. A::Foo(A&)_ 如果你这样做,它会起作用,但这会明确窃取。a.pareturn std::move(a.p);a.p

于 2016-10-01T15:11:41.067 回答