【问题标题】:Why is there no need to mark the move constructor of a type with a deleted copy constructor as deleted?为什么不需要将带有已删除复制构造函数的类型的移动构造函数标记为已删除?
【发布时间】:2020-10-03 20:00:18
【问题描述】:

考虑std::mutex。我明白为什么std::mutex 不应该是可移动的。但是它的复制构造函数被明确标记为已删除,但是我没有看到它的移动构造函数有这样的声明。那么为什么 cppreference 会说 std::mutex 不可移动呢?

根据文档 (https://en.cppreference.com/w/cpp/language/move_constructor),有许多未满足的先决条件会阻止隐式移动构造函数。但我找不到这个问题的原因。对于这个问题,我将不胜感激。

我真的不认为这个(en.cppreference.com/w/cpp/thread/mutex/~mutex) 是std::mutex 的用户定义析构函数。

【问题讨论】:

  • “有许多未满足的前提条件阻止了隐式移动构造函数。” - 你看的不够仔细。 mutex 有一个用户定义的析构函数。
  • @SebastianRedl &TonyTannous 但是 std::mutex 没有非默认析构函数。你能给我一些提示吗? 你的意思是这个(en.cppreference.com/w/cpp/thread/mutex/~mutex)是用户定义的析构函数吗?我很震惊。
  • 答案是它依赖于实现。并非所有实现都为互斥锁提供了一个简单的析构函数。
  • 你仍在尝试做你不应该做的事情。互斥锁不是 C++ 对象。它是一个封装在 C++ 类中的内核对象。请遵守相应的限制。
  • @michael chourdakis 从他的角度来看,cppref 只显示了默认析构函数,因此他对为什么未将 move 标记为已删除感到困惑。

标签: c++ c++11 constructor copy-constructor


【解决方案1】:

编译器不生成隐式移动构造函数有两个原因:

  • std::mutex 可能有一个用户定义的析构函数。在某些平台上,互斥对象分配内存,因此析构函数必须清理它,例如调用pthread_mutex_destroy()
  • 复制构造函数被显式删除,算作“用户声明”。

那么为什么标准的编写方式使得上述情况阻止了隐式移动构造函数的生成?首先考虑一个没有定义任何构造函数/析构函数/复制/移动运算符的类。然后整个类就表现为成员变量的集合。构造/破坏/复制/移动这样的集合时要做的合乎逻辑的事情就是单独对每个项目应用操作。但是,一旦您对这些操作进行了用户定义,您就为您的类添加了新的语义,并且基本上它不再只是成员变量的集合。编译器不够聪明,无法查看用户定义的操作如何隐式创建所有其他操作,因此安全的做法是不要隐式创建它们。

【讨论】:

  • 非常感谢您的慷慨帮助。我不是要怀疑你说的话。我只是想深入挖掘并确认。引用你的回答[强调我的]:复制赋值运算符被显式删除,算作“用户定义”。我刚刚听到这样一句话:“我认为删除算作它存在并参与在重载决议中,但它不能被调用”。因此,虽然它被删除了,但它仍然存在并且可以选择,这会使程序格式错误。” 有什么我可以参考的材料吗?我的意思是它是否在 C++ 标准中声明。
  • @John: "所以虽然它被删除了,但它仍然存在并且可以被选择,这会导致程序格式错误。" 这与是否移动构造函数是否隐式定义?规则是,如果你有一个用户定义的复制构造函数,你就不会得到一个隐式的移动构造函数。
  • @NicolBolas 但问题是std::mutex 是否有用户定义的复制构造函数。您会看到它被标记为已删除。你能告诉我原因吗?
  • @John: "但问题是 std::mutex 是否有用户定义的复制构造函数。" 不,不是。问题是为什么有一个显式删除的复制构造函数意味着你有一个隐式删除的移动构造函数。那是你问的。而且它不是“用户-定义”;它是“用户-声明的”;它就在您自己的链接中。
  • @NicolBolas 对不起。我不是母语人士。 :(。感谢您的耐心和帮助。请您告诉我为什么有一个显式删除的复制构造函数意味着有一个隐式删除的移动构造函数?我想了很久。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-20
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 2013-08-19
  • 2018-02-10
  • 1970-01-01
相关资源
最近更新 更多