将std::unique_ptr类成员标记为const

10

很多使用std::unique_ptr来管理类依赖关系的示例看起来像下面这样:

class Parent
{
public:
    Parent(Child&& child) :
    _child(std::make_unique<Child>(std::move(child))){}
private:
    std::unique_ptr<Child> _child;
};

我的问题是,将_child成员标记为const是否会产生任何意外的副作用?(除了确保在_child上不能调用reset()release()等函数之外)。

我之所以问是因为我还没有在示例中看到过这种情况,不知道这是有意为之还是只是为了简洁性/通用性。


1
问问自己:如果它是“const”,它能被移动吗? - NathanOliver
1
你是说有很多例子编译失败了吗?你展示的这个确实失败了。特别是,_child(std::move(child)) 这部分没有意义。 - Igor Tandetnik
4
尝试移动一个 constunique_ptr 会因为复制被禁止且没有接受常量右值引用的构造函数而无法编译通过。 - Hatted Rooster
@GillBates - 哦,是的,好主意!我感觉我漏掉了什么,但又说不上来 :) 请随意将其添加为答案,我会将其标记为正确的。 - Peet Whittaker
1
问问自己:耶稣会怎么做? - Leo Heinsaar
显示剩余4条评论
3个回答

8
由于std::unique_ptr(对对象进行唯一拥有权)的特性,它没有任何复制构造函数。 移动构造函数(6)只接受非const右值引用,这意味着如果您尝试让您的_childconst并进行移动,您将得到一个很好的编译错误 :)。
即使自定义的unique_ptr采用const右值引用,也无法实现。

为什么不应该呢?它并没有尝试复制或移动Parent的实例。 - Igor Tandetnik
@nwp 因为它通过移动const的child构造了一个unique_ptr - Hatted Rooster
1
@IgorTandetnik问题中没有说明Parent必须是可移动的,但它可以被制作成可移动的。 - nwp
1
并不多,正如@nwp所说,当前的实现允许移动到const unique_ptr中,但这会阻止Parent进行任何复制或移动操作,因为const unique ptr禁止了它。 - Hatted Rooster
3
楼主询问使成员变量 _child 成为 const 的“意外副作用”可能是什么。使 Parent 无法移动是一个楼主似乎没有预料到的副作用。 - Igor Tandetnik
显示剩余4条评论

1
缺点与任何const成员一样:赋值和移动赋值运算符不起作用(它们需要覆盖_child),从父级移动也不能窃取_child(性能错误)。此外,编写这样的代码是不常见的,可能会让人感到困惑。
收益微不足道,因为_childprivate的,因此只能从Parent内部访问,因此围绕更改_child而破坏不变量的代码量仅限于需要能够维护不变量的成员函数和友元。
我无法想象有哪种情况下获得的利益会超过缺点,但如果您确实这样做,则可以按照自己的方式进行,而不会破坏程序的其他部分。

好点。可能解释了为什么我以前在任何示例中都没有看到过它! - Peet Whittaker

1

是的,您可以这样做,这也是我在Qt中实现UI类时经常采用的方法:

namespace Ui {
    class MyWidget
}

class MyWidget : public QWidget
{
    Q_OBJECT  

    const std::unique_ptr<Ui::MyWidget> ui;

public:
    explicit MyWidgetQWidget *parent = 0)
        : ui(new Ui::MyWidget{})
    {
    }

    ~MyWidgetQWidget();
    // destructor defined as = default in implementation file,
    // where Ui::MyWidget is complete
}

这段代码与在C++03代码中编写Ui::MyWidget *const ui完全相同。
上述代码创建了一个新对象,但使用std::move()传入一个对象也没有问题。

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