从类方法返回成员unique_ptr

时间:2016-09-30 22:00:06

标签: c++ c++11 smart-pointers rvo

我正在尝试将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;
}

2 个答案:

答案 0 :(得分:11)

[class.copy]中的规则是:

  

[...]当return语句中的表达式是(可能是   括号内的 id-expression ,用于在主体中声明自动存储持续时间的对象或最内层封闭函数的 parameter-declaration-clause lambda-expression < / em>,重载解析到   选择首先执行复制的构造函数,就像对象是由右值指定一样。

在这个例子中:

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也不归。从它移动本身并不安全,因此语言不会尝试自动执行。

答案 1 :(得分:-1)

由于a.pstd::unique_ptr,因此无法复制,因此复制省略不适用(除其他原因外)。由于a.p的生命周期超出了A::Foo(A&)的主体,如果编译器自动尝试从a.p移动,那将会非常令人惊讶(例如编写代码的人会感到惊讶) ,这可能会破坏a的类不变量。如果您return std::move(a.p);,它会有效,但会明确窃取a.p