为什么 std::mutex 既不可复制也不可移动?

Why is std::mutex neither copyable nor movable?

有人能说说为什么std::mutex既不可复制也不可移动吗? 有人告诉我,这与避免资源浪费有一定关系。为什么 std::mutex 的复制构造函数应该被标记为已删除?如果没有,是否存在任何潜在问题?它的复制构造函数被明确标记为已删除,但我没有看到它的移动构造函数有这样的声明。那为什么cppreferencestd::mutex不可移动呢?

std::mutex 没有复制构造函数。如果您发送一份副本,每个人都会锁定自己的副本,并且您不会阻止竞争条件。帮助程序员不要搬起石头砸自己的脚。

30.4.1 Mutex requirements

A mutex object facilitates protection against data races and allows safe synchronization of data between execution agents (30.2.5). An execution agent owns a mutex from the time it successfully calls one of the lock functions until it calls unlock.

30.2.5 可锁定类型的要求 [thread.req.lockable]

An execution agent is an entity such as a thread that may perform work in parallel with other execution agents

如果你可以移动,那么所有权要求就被打破了。显然之前已经有人问过 why std::mutex doesn't have a move constructor?

在@Kabanus 上添加和引用草稿,回答为什么移动没有被标记为删除

12.8 复制和移动 class 个对象 [class.copy]

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

  • []
  • []
  • []
  • X 没有用户声明的析构函数
  • []

除了下面的答案之外,互斥句柄也不能在 OS 级别复制。在 Windows 中,您必须创建一个命名互斥体,然后根据该名称打开一个新句柄,例如,打算在新的 thread/process 中使用,而 std::mutex 从不创建命名对象.所以即使有拷贝构造函数,也不可能实现。

通常,同步对象的语义与其他一些普通对象的语义不同,即使它们显然被包装在 C++ 中 class

扩展一下其他答案,互斥锁等基本锁是提供原子操作的语言设计中最基本的对象,这里是lockunlock。这些可能拥有一个 OS 实现的句柄 (native_handle),它是硬件实现对象的句柄,甚至可能跳过中间句柄。

复制这样的句柄当然不是一件容易的事(你不能复制一个硬件,有时甚至是 OS 句柄,这很简单)。移动它可能更糟 - 移动会使对象处于未指定状态,但就其本质而言,互斥锁是跨线程共享的。如果你在一个线程上处理它,你将不得不以某种方式通知所有其他线程——更有可能你只会破坏代码。这是没有潜在好处的大量开销(我可以看到)。

关于为什么未在您的参考中明确删除移动构造函数 - 如果定义了(非默认)destructor12.8, comment 9,则不会创建默认 move 构造函数), 所以没有必要删除它。