【问题标题】:Returning member unique_ptr from class method从类方法返回成员 unique_ptr
【发布时间】:2016-09-30 22:00:05
【问题描述】:

我正在尝试将std::unique_ptr class 成员(尝试移动所有权)返回给调用者。以下是示例代码sn -p:

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

【问题讨论】:

  • 这是一个很好的第一个问题。欢迎使用 StackOverflow!

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


【解决方案1】:

[class.copy] 中的规则是:

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

在这个例子中:

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 也不属于此函数。离开它本质上并不安全,因此语言不会尝试自动执行它。

【讨论】:

    【解决方案2】:

    无法应用复制省略(除其他原因外),因为 a.p 是一个不可复制的 std::unique_ptr。而且由于a.p 的生命周期超出了A::Foo(A&amp;) 的主体,如果编译器自动尝试从a.p 移动,那将是非常令人惊讶的(就像编写代码的人一样),这可能会破坏a 的类不变量。如果你 return std::move(a.p);,它会起作用,但这会明确窃取 a.p

    【讨论】:

    • 复制省略可以应用于只移动类型(如 OP 的第二个示例)
    • @Barry 好的,我的第一句话是错误的。复制省略也适用于移动。但是,我相信其余的答案仍然有效。
    • @AndreKostur:由于编码器显式地尝试返回其复制构造函数已删除的 unique_ptr,因此唯一的选择是进行移动并且复制省略应该可以正常工作(IMO)。真正让我吃惊的是编译错误而不是警告。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 2011-04-16
    • 2015-04-16
    • 1970-01-01
    • 1970-01-01
    • 2019-08-26
    • 1970-01-01
    相关资源
    最近更新 更多