Meh*_*dad 120 c++ c++-faq move-semantics c++11
我很惊讶这没有显示在我的搜索结果中,我认为有人会在之前问过这个,考虑到C++ 11中移动语义的用处:
(原因等比与现有代码的兼容性问题,那是.)
Jon*_*ely 107
赫伯的答案(在编辑之前)实际上给出了一个不应该移动的类型的好例子:std::mutex
.
操作系统的本机互斥类型(例如,pthread_mutex_t
在POSIX平台上)可能不是"位置不变",这意味着对象的地址是其值的一部分.例如,操作系统可能会保留一个指向所有已初始化的互斥对象的指针列表.如果std::mutex
包含本机OS互斥类型作为数据成员并且本机类型的地址必须保持固定(因为操作系统维护指向其互斥锁的指针列表),则要么std::mutex
必须将本机互斥锁类型存储在堆上,以便它保留在在std::mutex
对象之间移动时的相同位置或std::mutex
不得移动.将它存储在堆上是不可能的,因为它std::mutex
有一个constexpr
构造函数,并且必须有资格进行常量初始化(即静态初始化),以便std::mutex
保证在程序执行开始之前构造全局,因此它的构造函数不能使用new
.所以剩下的唯一选择就是std::mutex
不动产.
相同的推理适用于包含需要固定地址的其他类型的其他类型.如果资源的地址必须保持固定,请不要移动它!
还有另一个不动的论点std::mutex
是,安全地执行它会非常困难,因为你需要知道没有人试图在移动时锁定互斥锁.由于互斥体是你可以用来防止数据竞争的构建块之一,如果它们不能安全地对抗种族本身就很不幸!对于一个不可移动的std::mutex
你知道任何人一旦构造它并且在它被销毁之前可以对它做的唯一的事情是锁定它并解锁它,并且这些操作明确保证是线程安全的并且不引入数据竞争.这个相同的参数适用于std::atomic<T>
对象:除非它们可以原子方式移动,否则不可能安全地移动它们,另一个线程可能正试图compare_exchange_strong
在移动它的那一刻调用该对象.因此,类型不应该是可移动的另一种情况是它们是安全并发代码的低级构建块,并且必须确保它们上的所有操作的原子性.如果对象值可能在任何时候被移动到一个新对象,你需要使用一个原子变量来保护每个原子变量,这样你就知道它是否可以安全使用它或者它已被移动......和一个原子变量来保护那个原子变量,等等......
我想我会概括地说,当一个对象只是一个纯粹的内存片段,而不是一个作为值或值的抽象的持有者的类型时,移动它是没有意义的.基本类型如int
无法移动:移动它们只是一个副本.你不能撕掉它的内容int
,你可以复制它的值,然后将它设置为零,但它仍然是int
一个值,它只是内存的字节.但是int
仍然可以在语言术语中移动,因为副本是有效的移动操作.但是,对于不可复制的类型,如果您不想或不能移动内存并且您也无法复制其值,那么它是不可移动的.互斥锁或原子变量是内存的特定位置(使用特殊属性处理)因此移动没有意义,也不可复制,因此它是不可移动的.
Her*_*ter 57
简短回答:如果类型是可复制的,它也应该是可移动的.然而,相反的情况并非如此:某些类型std::unique_ptr
是可移动的,但复制它们没有意义; 这些只是自然移动类型.
接下来会有更长的答案......
有两种主要类型(以及其他更特殊的类型,如特征):
类似值的类型,例如int
或vector<widget>
.这些代表值,自然应该是可复制的.在C++ 11中,通常你应该将move视为复制的优化,因此所有可复制类型都应该是可移动的...移动只是一种有效的方式来复制常见的情况,你不应该不再需要原始物体,无论如何都要破坏它.
存在于继承层次结构中的类似引用的类型,例如基类和具有虚拟或受保护成员函数的类.这些通常由指针或引用来保存,通常是一个base*
或者base&
,因此不提供复制结构以避免切片; 如果你想要像现有的那样获得另一个对象,你通常会调用一个虚函数clone
.这些不需要移动构造或赋值有两个原因:它们不可复制,并且它们已经具有更高效的自然"移动"操作 - 您只需将指针复制/移动到对象,而对象本身不会必须移动到新的内存位置.
大多数类型属于这两个类别之一,但也有其他类型的类型也很有用,只是更少见.特别是在这里,表示资源的唯一所有权的类型,例如std::unique_ptr
,自然是仅移动类型,因为它们不是类似值(复制它们没有意义)但是你直接使用它们(并非总是如此)通过指针或引用)所以想要将这种类型的对象从一个地方移动到另一个地方.
bil*_*llz 17
实际上当我搜索时,我发现C++ 11中的一些类型不可移动:
mutex
类型(recursive_mutex
,timed_mutex
,recursive_timed_mutex
,condition_variable
type_info
error_category
locale::facet
random_device
seed_seq
ios_base
basic_istream<charT,traits>::sentry
basic_ostream<charT,traits>::sentry
atomic
类型once_flag
显然有一个关于Clang的讨论:https://groups.google.com/forum/ ? fromgroups =#!topic / comp.std.c ++/pCO1Qqb3Xa4