一个已被移动的对象还能做什么?

165

标准是否明确定义了对象从移动后可以做什么?我曾经认为,你只能销毁一个已经移动的对象,但这是不够的。

例如,考虑标准库中定义的函数模板swap

template <typename T>
void swap(T& a, T& b)
{
    T c = std::move(a); // line 1
    a = std::move(b);   // line 2: assignment to moved-from object!
    b = std::move(c);   // line 3: assignment to moved-from object!
}

显然,必须能够对已移动的对象进行赋值操作,否则第2行和第3行将失败。那么我还能用移动后的对象做什么呢?在标准中我应该从哪里找到这些详细信息呢?

(顺便问一下,为什么第1行是T c = std::move(a);而不是T c(std::move(a));?)

2个回答

145

17.6.5.15 [lib.types.movedfrom]

C++标准库中定义的类型的对象可以被移动(12.8)。移动操作可以显式指定或隐式生成。除非另有规定,这些被移动的对象将被置于有效但未指定状态。

当一个对象处于未指定状态时,您可以对该对象执行任何没有前提条件的操作。如果存在带有前提条件的操作,您无法直接执行该操作,因为您不知道对象的未指定状态是否满足前提条件。

通常没有前提条件的操作示例:

  • 析构
  • 赋值
  • const观察器,例如getemptysize

通常具有前提条件的操作示例:

  • 解引用
  • pop_back

此答案现在以视频格式出现在此处:http://www.youtube.com/watch?v=vLinb2fgkHk&t=47m10s


6
只要这些检查本身没有前提条件,当然可以这样做。 - Christian Rau
2
@Chris:但这与普通的未移动对象有何不同? - fredoverflow
2
@FredOverflow:唯一的区别是大多数操作会使对象处于指定状态。移动后的状态未指定,但除此之外并没有特殊之处。当然,如果您是类的作者,可以自由地将移动后的状态设置为您想要的特殊状态。但请注意,如果您在std算法中使用您的类,则无论是否处于移动后的状态([utility.arg.requirements],表20和22),都需要满足std算法的规定要求。 - Howard Hinnant
2
也许应该成为一个单独的问题,但这是否意味着:如果我有一个带有char * buffer;int length;成员的字符串,那么我的移动构造函数/赋值必须交换(或设置)两者的值?或者,如果未指定长度(即emptysize返回无意义的值),是否可以? - UncleBens
4
你的意思不通。一个 C++03 类并不会“违反 C++0x 标准”,因为只有在生成移动构造函数的情况下才会违反标准。而 C++03 代码不会移动该类,因此没有理由生成移动构造函数。 - MSalters
显示剩余14条评论

65
移动来源对象存在于未指定但有效的状态。这意味着,虽然对象可能不能再做什么了,但它的所有成员函数(包括operator=)应该仍然表现出已定义的行为,并且所有成员都处于已定义的状态,仍需要销毁。标准没有给出具体的定义,因为这对每个用户自定义类型都是独特的,但您可以找到标准类型的规格说明。有些像容器这样的类型相对明显,它们只是在它们的内容周围移动,并且一个空容器是一种良好定义的有效状态。原始数据类型不修改移动来源对象。
附注:我认为应该使用T c = std::move(a),这样如果移动构造函数(如果没有提供移动构造函数,则使用复制构造函数)是显式的,该函数将失败。

29
并非所有的成员函数都有明确定义的行为,只有那些没有先决条件的函数才是如此。例如,您可能不想从已移动的vector中pop_back,但您可以确定它是否为空(empty())。 - Howard Hinnant
7
@Howard Hinnant:无论如何,从空的vector中执行pop_back操作都是未定义行为,所以我非常确定从移动后的vector中执行pop_back操作也是一致的未定义行为。 - Puppy
14
我们正在讨论被移动的对象,而不是已知处于空状态的对象。被移动的对象具有未指定的状态(除非另有规定)。[lib.types.movedfrom] - Howard Hinnant
6
@Howard 没有具体指定,但是有效,因此 pop_back 的行为与任何有效向量相同(即使是空向量)。 - Christian Rau
1
定义的行为并不意味着有一个定义好的结果。因此,以一个被移动过的 vector 为例,我们可以找出它是否为空 (empty()),但不能期望它返回一个特定的结果,无论是 true 还是 false。 - Simon
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接