C++ 影响正确性的移动构造函数/赋值运算符示例?

C++ 影响正确性的移动构造函数/赋值运算符示例?,c++,c++11,C++,C++11,我想知道是否有任何不实现移动语义的例子,例如,移动构造函数和移动赋值操作符会影响程序的正确性,而不仅仅是它的性能。我将假设你暗示没有办法复制这个类 例如std::unique\u ptr。因为这个指针应该是动态分配给new的对象的唯一所有者,所以允许一个复制构造函数没有任何意义,因为我们不能有两个所有者。但是,移动赋值运算符和移动构造函数的实现仍然允许在已分配的对象上移动。因为我们正在移动,从唯一移动的\u ptr将不再拥有该对象的所有权,但现在移动到唯一的\u ptr拥有该对象。这并不违反唯一

我想知道是否有任何不实现移动语义的例子,例如,移动构造函数和移动赋值操作符会影响程序的正确性,而不仅仅是它的性能。

我将假设你暗示没有办法复制这个类

例如std::unique\u ptr。因为这个指针应该是动态分配给new的对象的唯一所有者,所以允许一个复制构造函数没有任何意义,因为我们不能有两个所有者。但是,移动赋值运算符和移动构造函数的实现仍然允许在已分配的对象上移动。因为我们正在移动,从唯一移动的\u ptr将不再拥有该对象的所有权,但现在移动到唯一的\u ptr拥有该对象。这并不违反唯一所有权部分。如果我们没有这些移动语义,那么unique_ptr的用例就会少得多


更一般地说,只要您不尝试在没有移动赋值操作符/移动构造函数的情况下移动对象,那么从技术上讲,就永远不会出现影响程序正确性的情况。非常特定的类有这种不可复制/不可移动的限制,例如std::mutex,因为这些类这样做没有意义。请注意,C++17之前版本仅从函数返回一个对象就要求它既可以复制也可以移动,因此这些类的用例是有限的。

这将是任何值类型,其中实现复制语义是以下一种或多种:

非理性的:与课程设计背道而驰的

不可能

产生可能导致程序终止/死锁的成本

非理性类别适用于unique_ptr这样的类型,类型的逻辑设计使得复制变得毫无意义

不可能的类别包括存在于系统之外的许多API的包装器类型

考虑某个OpenGL对象的包装器类型。您可能会认为,因为OpenGL是一个可查询的API,所以您可以查询其中设置的每个状态,通过从旧对象获取状态并在新对象中设置状态,您可以合理地实现对象复制

但这是有缺陷的,因为OpenGL扩展。它们可以为对象提供新的状态,而在这些扩展之前编写的代码对此一无所知

最后一类听起来很奇怪。但让我们接受OpenGL包装器的想法。假设您实现了复制。您正在将缓冲区对象复制到GPU内存的一部分

嗯。。。你在哪里复印?您是在具有当前OpenGL上下文的线程上执行此操作的吗?该对象是否在另一个线程中同时更新,这样您就在其他地方,这样您就创建了竞争条件

您可以尝试使用互斥来避免竞争条件,但现在您已经创建了死锁的真正可能性。为什么?因为许多对象包含缓冲区对象。复制这些对象自然需要复制它们的包含缓冲区。因此,您有一个多拷贝操作,该操作必须锁定多个互斥体才能完成其工作。如果复制的是相同的缓冲区,则可能会与其他多拷贝操作发生死锁

所以要么你冒险死锁,要么你冒险得到不一致的内存拷贝。。。或者你根本不允许复制


如果您的类型不允许复制,那么除非该类型的设计另有说明,否则您应该允许移动。

您的问题是假设该假设类型正确实现了复制语义吗?@Nicolas任何有助于查找示例的假设我不确定为什么否决?我认为这是一个合理的问题,它指出,与第三条规则不同,未能提供移动构造函数和移动分配通常不是错误,而是错过了优化机会。我徘徊在通常部分通常不->从不复制con不存在可能有错误。这有点开放,但对象管理底层资源的情况就是一个很好的例子。即使假设编译器省略了一个副本分配给一个副本,也会有一个点,即资源在两个对象中处于活动状态。在这种情况下,显式禁用复制/分配非常有意义。基于@Nicolabolas的答案,我有简单的OpenGL着色器和程序类,它们明确定义了“移动”复制/分配,因为我从来不需要复制。我可以通过右值来传递这些东西——简化我的设计。