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