使 class 不可移动的用例(如果有)是什么?

what are use cases (if any) to make a class non-moveable?

考虑使用 classic 方法使 class 不可复制:

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

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

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

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

现在让我们从标准库中寻找 classic noncopyable classes,例如unique_ptr。它不可复制但可移动,否则它的实用性将受到限制。我想对于许多其他情况也是如此。

实际上,我无法想出任何 class 应该设为不可移动的例子。我的论点是:即使 class 不打算移动,也有很小的可能是由于可能造成伤害的错误造成的。

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

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

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

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

不起作用 (wandbox) - 无法生成移动操作,因为它们不是为基础 class 生成的。您需要手动实施它们 -> 列出您的成员 -> 维护 -> 错误。只需在 noncopyable 中默认移动操作即可解决问题。

2) 可移动性有害且应该避免的用例有哪些?

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

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

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

现在,任何仍然引用 a 的人都指向一个陈旧的对象或(完全是其他的东西)。

出现这种情况的其他情况是当您具有取决于对象标识 ("address stability") 的外部逻辑时,例如使用 pthread 互斥锁:Move constructor for std::mutex

附录 - 命名约定 scoped_XXXX 通常用于暗示不可移动的类型(即保证不可变实例标识的类型)。将此与例如unique_XXXX 这意味着一个独特的实例,可以四处移动。 注意 然而,标准库并没有完全采用这种命名约定:示例:std::lock_guard<> vs std::unique_lock<>。 C++17amends this一点

1) Why boost::noncopyable is non moveable also?

很有可能,历史原因。 boost::noncopyable 应该适用于 C++11 之前的编译器。在使用 C++11 及更高版本时,实际上没有什么理由从 boost::noncopyable 继承。如果它在 C++11 中是可移动的,我们如何在 C++03 编译器中建立相同的 move 语义?

2) What are use cases when moveability is harmful so it should be prevented?

不可移动性应适用于假定作为外部系统提供给您的各种资源 - 例如同步原语、代表计算机 ACPI 平台等内容的对象等。

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

来自 C++11 标准:

If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

— X does not have a user-declared copy constructor,

— X does not have a user-declared copy assignment operator,

— X does not have a user-declared move assignment operator,

— X does not have a user-declared destructor, and

— the move constructor would not be implicitly defined as deleted.

所以,我猜当你 delete 你的复制 c'tor and/or 复制赋值时,你阻止了默认移动构造函数的声明。

可移动性有害且应该避免的用例有哪些?

我首先想到的是依赖于它们在内存中的位置的对象 space,例如 mutexes。