bec*_*cko 61 c++ language-lawyer c++11 c++14
我们x是某种类型的先前已初始化的变量.是以下行:
x = std::move(x)
Run Code Online (Sandbox Code Playgroud)
未定义?这个标准在哪里?它对它有什么看法?
Sha*_*our 53
不,这不是未定义的行为,它将是实现定义的行为,它将取决于如何实现移动分配.
与此相关的是LWG问题2468:图书馆类型的自动分配,请注意这是一个活跃的问题,并且没有正式的提案,所以这应该被视为指示性的而不是确定的,但它确实指出了涉及的部分对于标准库并指出它们目前存在冲突.它说:
假设我们写
Run Code Online (Sandbox Code Playgroud)vector<string> v{"a", "b", "c", "d"}; v = move(v);v的状态应该是什么?该标准没有说明任何关于自动转让的具体内容.标准的几个部分中有相关的文字,并且不清楚如何协调它们.
[...]
从文本中不清楚如何将这些碎片放在一起,因为不清楚哪一个优先.也许17.6.4.9 [res.on.arguments]获胜(它强加了MoveAssignable要求中未提及的隐式前提条件,因此v = move(v)未定义),或者可能是23.2.1 [container.requirements.general ] wins(它显然为Container :: operator =提供了额外的保证,超出了一般保证库函数的保证,所以v = move(v)是no-op),或者其他的东西.
在我检查的现有实现上,为了它的价值,v = move(v)似乎清除了向量; 它没有保持向量不变,也没有导致崩溃.
并建议:
非正式地:更改MoveAssignable和Container需求表(以及提及移动分配的任何其他需求表,如果有的话),以明确x = move(x)是已定义的行为,并使x保持有效但未指定的状态.这可能不是标准今天所说的,但它可能与我们的预期相符,并且与我们告诉用户的内容以及实际执行的内容一致.
注意,对于内置类型,这基本上是一个副本,我们可以从草案C++ 14标准部分5.17 [expr.ass]中看到:
在简单赋值(=)中,表达式的值替换左操作数引用的对象的值.
这与课程的情况不同,其中5.17说:
如果左操作数是类类型,则类应完整.对类的对象的赋值由复制/移动赋值运算符(12.8,13.5.3)定义.
注意,clang有一个自动警告:
日志:向Clang添加一个新警告-Wself-move.
-Wself-move与-Wself-assign类似.尝试将值移动到自身时会触发此警告.有关此警告可能已捕获的错误,请参阅r221008.
Yak*_*ont 11
所有这一切都是调用X::operator=(X&&)(左值限定" *this").
在原始类型上,std::move几乎没有兴趣,也根本不与之交互=.所以这只适用于类类型的对象.
现在,对于一个类型std(或由其中一个模板生成),对象moved往往处于未指定(但有效)的状态.这不是未定义的行为,但它不是有用的行为.
X::operator=(X&&)必须检查每个给定的语义,检查其中的每个类型std对于堆栈溢出答案来说"太宽泛".他们甚至可能会自相矛盾.
一般来说,当move您从一个物体进入时,您正在与消费者沟通"您不关心该物体后来处于什么状态".使用的x = std::move(x)是这样不礼貌的,因为你(通常情况下)不关心什么状态x是在操作完成后(因为你是分配给它).您在同一操作中使用与左值和右值相同的对象,这不是一种好习惯.
一个有趣的例外是默认值std::swap,它是这样的:
template<class T>
void swap(T& lhs, T& rhs) {
T tmp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(tmp);
}
Run Code Online (Sandbox Code Playgroud)
如果你在同一个对象上调用swap两次lhs = std::move(rhs),那么中间行会执行x = std::move(x).
但请注意,x在完成此行之后,我们并不关心处于什么状态; 我们已经存储了xin 的状态tmp,我们将在下一行恢复它.