什么是用例(如果有的话)使一个类不可移动?

时间:2017-10-10 11:52:19

标签: c++ boost move

考虑一种使类不可复制的经典方法:

// similar to boost::noncopyable
class noncopyable
{
protected:
    constexpr noncopyable() = default;

    noncopyable(const noncopyable&) = delete;
    noncopyable& operator=(const noncopyable&) = delete;
};

class c: private noncopyable
{ /* ... */ };

由于声明任何复制操作会阻止自动生成移动操作,因此这会自动使所有派生类都不可移动(默认情况下)(因此noncopyable的全名将为noncopyable_and_nonmoveable)。

现在让我们从标准库中寻找经典的不可复制的类,例如: unique_ptr。它不可复制但可移动,否则其效用将受到限制。我认为对于许多其他情况也是如此。

实际上,当一个班级不可移动时,我无法想出任何一个例子。我的论点是:即使没有计划将一个班级移到那里,也有可能通过一个可能造成伤害的错误来完成。

实际上是两个相关的问题:

1)为什么boost::noncopyable也不可移动?这会导致问题,请考虑:

struct c: private boost::noncopyable
{
    std::unique_ptr<Something> something;

    c(c&&) = default;
    c& operator=(c&&) = default;
};

不起作用(wandbox) - 无法生成移动操作,因为它们不是为基类生成的。您需要手动实现它们 - &gt;列出你的会员 - &gt;维护 - &gt;错误。只需在noncopyable中默认移动操作就可以解决问题。

2)什么是可移动性有害的用例,应该防止它?

3 个答案:

答案 0 :(得分:8)

不可移动使您可以更好地控制对象标识。

如果一个物体是可移动的,它的地址可以改变:

moveonly a;
b = std::move(a);

现在,任何人仍然可以引用a指向陈旧的对象或(完全不同的东西)。

出现这种情况的其他情况是,如果你愿意,你可以根据对象的身份(“地址稳定性”)拥有外部逻辑,例如:使用pthread互斥锁:Move constructor for std::mutex

附录 - 命名约定scoped_XXXX常用于暗示不可移动的类型(即保证不可变实例标识的类型)。将其与例如unique_XXXX表示可以移动的唯一实例。 注意 但是,标准库并未完全采用此命名约定:示例:std::lock_guard<> vs std::unique_lock<>。 C ++ 17 amends this一点

答案 1 :(得分:2)

  

1)为什么boost :: noncopyable也不可移动?

极有可能,历史原因。 boost::noncopyable应该与pre-C ++ 11编译器一起使用。在使用C ++ 11及更高版本时,实际上没有理由继承boost::noncopyable。如果它可以在C ++ 11中移动,我们如何在C ++ 03编译器中建立相同的 move 语义?

  

2)什么是可移动性有害的用例,应该防止它?

不可移动性应该适用于所谓的外部系统资源 - 例如同步原语,代表计算机和ACPI平台之类的东西的对象等。

答案 2 :(得分:0)

为什么boost :: noncopyable也不可移动?

来自C ++ 11标准:

  

如果类X的定义没有显式声明移动构造函数,则会隐式声明一个   当且仅当

时默认为      

- X没有用户声明的复制构造函数,

     

- X没有用户声明的复制赋值运算符

     

- X没有用户声明的移动赋值运算符

     

- X没有用户声明的析构函数,

     

- 移动构造函数不会被隐式定义为已删除。

所以,我想当你delete复制c&#tor;和/或复制赋值时,你就会阻止默认移动构造函数的声明。

什么是可移动性有害的用例,应该防止它?

我想到的第一件事是依赖于它们在内存空间中的位置的对象,例如mutex es。