自我移动赋值的行为

9

示例代码:

#include <iostream>

int main()
{   
    std::vector<int> w(20, 123), x;
    w = std::move(w);
    std::cout << w.size() << std::endl;
}

在g++ 4.8.3上的输出为:0

当然,标准规定移动赋值运算符将操作数留在未指定状态。例如,如果代码是x = std::move(w);,那么我们希望w.size()为零。

然而,是否有一个指定的排序或其他条款涵盖了自我移动案例?这是不确定的,大小是0还是20,或者其他什么,或者是未定义的行为?标准容器在这里有任何定义的语义吗?

相关:此线程讨论了您是否应该关心自我移动在自己的类中,但没有讨论标准容器的移动赋值运算符是否如此,并且没有提供标准引用。

NB. 这是否与w = static_cast< std::vector<int> && >(w);完全相同,或者std::move是一个函数是否会有所不同?


2
有趣。在§23.2.1中的表96中指出,当a是容器类型的值且rv是容器类型的非const右值时,a = rv;的操作语义为“a的所有现有元素都将被移动分配或销毁”,后置条件为“此赋值之前,a应等于rv的值”。在自我移动赋值的情况下,很难同时满足这两个条件。 - T.C.
问题已经在这里得到回答:https://dev59.com/8mcs5IYBdhLWcg3wHwNH#13127916 - 101010
1
@40two,这个问题本身被标记为“重复”,但我链接的那个问题绝对不是重复的!(我猜测你链接的问题没有出现在我的搜索结果中,因为搜索会跳过重复项)。我们能否请求将您链接的问题取消标记为“重复”,因为它实际上有有用的讨论。 - M.M
@MattMcNabb 请看一下这个答案。在我看来,它回答了你的问题。 - 101010
@40two,你提供的答案确实回答了我的问题;然而我想说的是,那个答案所回答的问题被标记为重复,但实际上它并不应该被标记为重复。(或者说曾经被标记为重复,现在已经重新打开了) - M.M
1
@MattMcNabb,有人重新打开了它。在我看来,这个问题被错误地关闭为重复问题。 - 101010
1个回答

7

§17.6.4.9 [res.on.arguments]:

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

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

由于“实现可以假定该参数是对此参数的唯一引用”,自我移动赋值会违反这个假设,因此它具有未定义的行为。

另请参见LWG问题1204


第一个“Note:”似乎已经涵盖了w = std::move(w);的情况,除非我误解了那个注释?这是关于将w绑定到std::move参数的讨论。 - M.M
1
@MattMcNabb,你上一个问题的答案是“是”。在这种情况下,你的static_cast与std::move具有相同的效果。 - juanchopanza
好的,那么结论是无论t的类型是什么,t = std::move(t);都是未定义行为? - M.M
如果t是标准库类型,那么看起来是这样的。 - T.C.
1
如果用户定义的类型实现了自己的移动赋值运算符,那么它就不是未定义行为(UB)。如果一个用户定义的类型有一个默认的移动赋值运算符,那么它取决于成员变量或基类(可能是模板!)中是否有任何标准库类型... - M.M
显示剩余10条评论

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