请考虑以下代码。
using T = std::string;
void rotate_left(std::vector<T>& v) {
T temp = std::move(v[0]);
for (size_t i=0; i+1 < v.size(); ++i) {
v[i] = std::move(v[i+1]);
}
v.back() = std::move(temp);
}
int main()
{
std::vector<T> v(3); // a vector of three Ts
T x = std::move(v[1]); // move-from the second element
rotate_left(v);
// Can we now say that v[0] is in a moved-from state, or did we
// get undefined behavior when we moved from v[1] a second time?
}
rotate_left
函数只是简单地将向量中的所有内容向下移动一个位置(然后将第一个元素放到末尾)。我的问题是,当向量中的一个元素处于“移动状态”时,此函数是否已定义行为?
这与“自动移动”有关但不完全相同。在这种情况下,我们正在从一个移动的对象移动到另一个移动的对象,我的问题是我们是否可以依赖于这两个对象仍然处于某些移动状态,或者“赋值来自”是否是其中一个操作“具有前提条件”,因此不能用于任意移动的对象。
我很清楚
unique_ptr
所以我真正想要的是:
我从this bug推断_GLIBCXX_DEBUG
检查是否自行移动,但我可以确认它不会检查移动移动;这是因为他们认为从移动到移动是安全合法的,还是因为没有人编写代码来检查它呢?
答案 0 :(得分:5)
您的程序的行为定义良好,但未指定。区别在于v
具有有效状态,您只是不知道该状态是什么而没有检查它。
标准的这一角落一直存在争议,而且措辞难以理解。但我相信最新的措辞(C ++ 17)是我们迄今为止的最佳尝试,所以我将引用它(N4660)。
免责声明,我实际上与N4659有关,因为N4660尚未公开发布。差异是无关紧要的。
从20.5.5.15 从库类型的移动状态[lib.types.movedfrom]
1可以从(15.8)移动C ++标准库中定义的类型的对象。可以显式指定或隐式生成移动操作。除非另有规定,否则此类移动对象应置于有效但未指定的状态。
此段落是由std :: lib定义的所有类型的一揽子声明,它们从对象移动不是“毒药”,你只是不知道它们的价值。
此外,标准中定义的每个算法(包括成员函数)可能具有在调用该算法之前必须为真的前提条件列表。如果没有列出前置条件,则表示您始终可以调用该函数。
对std :: lib中定义的所有类型的移动赋值永远不会为左侧或右侧参数列出任何前提条件。
很难引用一些不存在的东西,但这就是这个规范的工作方式。
进一步概括,以下部分引用了与std :: lib一起使用的所有类型(即用户提供的):
20.5.3.1模板参数要求[utility.arg.requirements]
表23 - MoveConstructible要求[moveconstructible]
T u = rv;
T(rv);
rv
的状态在后置条件中未指定。
表25 - MoveAssignable要求[moveassignable]
t = rv
仅当t
和rv
未引用同一对象时,t
相当于分配前rv
的值。
之后,rv
的状态未指定(t
和rv
是否指向同一对象。)
此外,本节注释(注释是非规范性的,通常是规范性文本出现在其他地方)rv
必须仍然满足它所使用的库组件(算法)的要求,即使它已被使用离开了。
例如,允许std :: sort从值x
移动,然后在比较表达式中使用x
。 x
必须是LessThanComparable,无论x
是否被移动。只有x
的值未指定。
bool b = x < x; // b must be false, no matter what!