标准库对于自我移动赋值有什么保证?

57

C++11标准在与标准库相关的自身移动赋值方面有什么规定?更具体地说,如果有的话,对于selfAssign做了什么是有保证的吗?

template<class T>
std::vector<T> selfAssign(std::vector<T> v) {
  v = std::move(v);
  return v;
}

4
@Mark 我不认为这是重复的问题。那个问题是关于如何编写自己的代码,而这个问题是关于标准库保证的内容。 - R. Martinho Fernandes
2
请注意,这不涉及 T 的自我移动分配。 - Xeo
@Xeo 我已经删除了那行代码。感谢你指出错误。 - Bjarke H. Roune
2
投票重新开放,因为标记为“重复”的帖子并未涵盖标准容器的移动赋值行为保证,并且没有一个答案提供像Howard Hinnant在此主题中的回答那样的标准参考。 - M.M
2个回答

46

17.6.4.9 函数参数 [res.on.arguments]

1 除非另有明确规定,否则以下规定适用于 C++ 标准库中定义的所有函数参数。

...

  • 如果函数参数绑定到一个右值引用参数,则实现可以假设该参数是对此参数的唯一引用。[注意:如果参数是形如 T&& 的通用参数,并且绑定了类型为 A 的左值,则该参数绑定到一个左值引用(14.8.2.1),因此不受前面一句话的限制。—注解] [注意:如果程序将左值转换为 xvalue 并在将该左值传递给库函数时(例如通过使用 move(x) 调用函数),程序实际上要求该函数将该左值视为临时变量。实现可以优化掉可能需要的别名检查,如果该参数是一个左值的话。—注解]

因此,实现std::vector<T, A>::operator=(vector&& other)被允许假设other是一个prvalue。如果other是一个prvalue,则不可能进行自我移动赋值。

可能发生的情况是:

v将处于无资源状态(容量为0)。如果v已经具有0容量,则这将是一个无操作。

更新

最新工作草案,N4618已经修改以明确说明在MoveAssignable要求中的表达式:

t = rv

(其中rv是一个右值),如果trv没有引用同一对象,则t只需要是赋值之前rv的等效值。无论如何,rv在赋值后的状态都是未指定的。这里有一个额外的注释以进一步澄清:

rv仍然必须符合使用它的库组件的要求,无论trv是否引用同一对象。


6
如果我正确理解了你的引用,那么函数selfAssign由于未满足移动赋值运算符的前置条件而会调用未定义的行为。我的理解是,在调用std::swap(v, v)时,std::swap将对自我移动赋值。这是否意味着调用std::swap(v, v)是未定义的行为? - Bjarke H. Roune
有趣。这意味着&&对于参数来说暗示了__restrict; 我想知道是否有任何编译器已经利用了这一点。 - Matthieu M.
3
这只是标准关于它自身函数的说法,这并不是关于 && 参数的一般性声明。在您编写的代码中,您不会受到此限制。 - Nicol Bolas
@BjarkeH.Roune:swap(v, v) 是合法的,并且需要正常工作。通常情况下,它会正常工作,因为自我移动赋值是使用已移动的值完成的(假设我们不谈论具有专门自我交换的向量)。在通用的自我交换中,您正在从未指定的值移动分配到未指定的值。因此,只要不崩溃,发生什么并不重要。swap(t, t) 要求 TMoveAssignable。即使 T 处于已移动状态,该要求仍然成立。 - Howard Hinnant
@HowardHinnant 我不太明白你的理由。我在这个主题中提出了一个问题:如何使用移动赋值和移动构造函数来实现std::swap - Bjarke H. Roune
请注意,不保留其状态的自我移动分配容器将违反对于(基于分配器的)移动分配容器不使迭代器失效的建议要求:https://cplusplus.github.io/LWG/lwg-active.html#2321 - Emile Cormier

5

Eric Niebler的相关帖子,其中包含多个链接,例如Howard Hinnant的这个答案

最新的C++20工作草案(N4861)对我来说在这个问题上仍然有点模糊。然而,最近有一个Library Working Group issue 2839,在[lib.types.movedfrom]/2中添加了以下明确的声明:

在C++标准库中定义的类型的对象可以移动分配(11.4.6 [class.copy.assign])给自身。这样的赋值会将对象置于有效但未指定的状态,除非另有规定。

这已经出现在C++23的N4885工作草案中了。

因此,selfAssign保证不会导致未定义的行为,并且由于std::vector没有额外的保证,因此将v留在某个有效状态。


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