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

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

本文关键字:如果 可移动 什么      更新时间:2023-10-16

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

// 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)-不能生成移动操作,因为它们不是为基类生成的。您需要手动实现它们->列出您的成员->维护->bug。只要在noncopyable中默认移动操作就可以解决问题。

2) 当可移动性是有害的,因此应该加以预防时,有哪些用例?

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

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

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

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

出现这种情况的其他情况是,如果愿意,您有依赖于对象标识的外部逻辑("地址稳定性"),例如pthread互斥:移动std::mutex 的构造函数

附录-命名约定scoped_XXXX经常用于暗示不可移动的类型(即保证不可变实例标识的类型)。与此形成对比的是,例如unique_XXXX,它意味着一个独特的实例,可以四处移动注意但是,标准库并没有完全采用此命名约定:例如:std::lock_guard<>std::unique_lock<>。C++17对此进行了一点修正

1)为什么boost::non-copyable也是不可移动的?

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

2)当可移动性是有害的,因此应该加以预防时,有哪些用例?

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

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

来自C++11标准:

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

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

--X没有用户声明的副本分配运算符

--X没有用户声明的移动分配运算符

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

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

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

当可移动性是有害的,因此应该加以预防时,有哪些用例

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