const unique_ptr成员的返回值优化不起作用?

3

我有一个类Whole,通过unique_ptr持有一个类Part的对象。由于我没有为Whole提供复制构造函数,因此由于unique_ptr成员,复制构造函数被删除。以下是代码:

class Part {
};

class Whole {
public:
    Whole(std::unique_ptr<Part> part) : part(std::move(part)) {
    }
private:
    const std::unique_ptr<Part> part; //yields error later!
    //std::unique_ptr<Part> part; //ok!
};

我希望通过一个名为build()的工厂函数创建一个完整的实例。
Whole build() {
    auto part = std::unique_ptr<Part>{new Part{}};
    return Whole{std::move(part)};
}

我希望将它用于以下方式:

int main() {
    auto whole = build();
}

只要不将Whole的Part成员的unique_ptr声明为const即可正常工作。据我所知,这是由于返回值优化防止了临时副本的创建和复制。然而,如果我将Whole::part声明为const,我的编译器会报告调用已删除的复制构造函数的问题。为什么不能使用const声明?或者这段代码有问题吗?
我正在使用GNU编译器版本:(Ubuntu 4.8.4-2ubuntu1〜14.04.1)4.8.4

复制省略不是强制性的。 - Lingxi
3
移动对象将修改现有对象。通过标记成员为const,您明确将其设置为不可修改。 - Revolver_Ocelot
2
也许你想使用 std::unique_ptr<const Part> part; - juanchopanza
有一个隐式移动构造函数。它与复制省略无关。 - Simple
1个回答

8

const std::unique_ptr 禁止移动类 Whole 的构造函数。

因此,auto whole = build(); 是无效的。(即使调用被省略,该调用也应该有效)

在 C++17 中,我们有了保证复制省略的特性,消除了该限制,使代码更正确。


好的,那很有道理。如果一个类有一个std::unique_ptr成员而没有提供自定义的复制/移动等构造函数,这是不好的实践吗?似乎必须记住不要将成员声明为const,以便移动构造仍然可行。 - user1304680
1
如果您有std::unique_ptr,则不会生成复制构造函数(因为来自std::unique_ptr的一个已被删除),但移动构造函数仍然会生成。因此,该类可以正确使用。在我看来,显式默认移动(Whole(Whole&&)= default;)并删除复制(Whole(const Whole&)= delete;)(以及分配相同)将更易读。 - Jarod42
1
@tabata 先生,复制构造函数在这里不适用,您的修改是误导性的。即使 OP 的代码能够正常工作,也禁止使用复制构造函数,因为 unique_ptr 必须是唯一拥有的。build() 返回右值,然后 auto whole = build() 调用了 Whole移动构造函数。但是 const 禁止 移动构造函数,因为它将 Whole::part 更改为 nullptr。因此编译失败。这就是 Jarod42 说的。顺便说一下,从 C++17 开始,在这种情况下保证 RVO,原始代码变得很好。 - Hiroki
从技术上讲,在C++17中,这种情况下没有复制省略(RVO)。相反,没有临时材料被实例化并且whole直接被构造。因此,即使移动构造函数被删除,在C++17中程序也是正常的:https://wandbox.org/permlink/QXZgSfjKHkhJOoxc。 - Daniel Langr
https://godbolt.org/z/xTaz63z68 - Marek R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接