标准库中自赋值不安全移动赋值运算符的基本原理是什么?

What is the rationale for self-assignment-unsafe move assignment operators in the standard library?

关于移动分配的标准图书馆政策是the implementation is allowed to assume that self-assignment will never happen;在我看来,这似乎是一个非常糟糕的主意,因为:

那么,为什么要做出这样的决定?


¹ 特别是在库代码中,实现者可以自由地利用有关 "branch expected outcome" 的编译器特定提示(想想 gcc 中的 __builtin_expect / VC++ 中的 __assume) .

std 中的对象移动应该在重用之前被丢弃或分配给。 任何除此之外不完全免费的内容均不予承诺。

有时候东西是免费的。就像一个移动构造的容器是空的。请注意,某些 move-assiged-from 案例没有这样的保证,因为某些实现可能会选择移动元素而不是缓冲区。为什么不同?一个是免费的额外保证,另一个不是。

支票或其他支票并非完全免费。占用了一个分支预测槽位,即使预测也只是差不多空闲。

最重要的是,a = std::move(a); 是逻辑错误的证据。 Assign-from a(within std)意味着你只会赋值给或丢弃a。然而在这里你希望它在下一行有特定的状态。要么你知道你在自我分配,要么你不知道。如果你不这样做,你现在正在从你也在填充的对象移动,而你不知道它。

"do little things to keep things safe"的原理与"you do not pay for that which you do not use"的原理冲突。在这种情况下,第二个赢了。

Yakk gives a very good answer(像往常一样,投票赞成)但这次我想补充一点信息。

self-move-assignment 的政策与过去 half-decade 相比发生了一点点变化。我们最近刚刚在 LWG 2468 中澄清了这个极端情况。实际上,我应该更准确地说:会议之间的一个非正式小组同意解决这个问题,并且很可能在下个月(2016 年 11 月)投票进入 C++1z 工作草案。

问题的要点是修改MoveAssignable要求以明确如果移动分配的目标和来源相同object,则对值的要求没有要求赋值后的 object (除非它必须是有效状态)。进一步说明如果这个object是和std::lib一起使用的,它必须仍然满足算法的要求(例如LessThanComparable是否[=59] =] 是 move-assigned 甚至是自我 move-assigned.

所以...

T x, y;
x = std::move(y);  // The value of y is unspecified and x == the old y
x = std::move(x);  // The value of x is unspecified

xy 仍处于有效状态。没有内存泄漏。没有发生未定义的行为。

这个职位的理由

还是表演。然而,人们认识到 swap(x, x) 自 C++98 以来一直是合法的,并且确实在野外发生。此外,由于 C++11 swap(x, x)x:

执行自移动赋值
T temp = std::move(x);
x = std::move(x);
x = std::move(temp);

在 C++11 之前,swap(x, x) 是(一个相当昂贵的)no-op(使用复制而不是移动)。 LWG 2468 阐明在 C++11 及之后的版本中,swap(x, x) 仍然是(不是那么昂贵)no-op(使用移动而不是复制)。

详情:

T temp = std::move(x);
// temp now has the value of the original x, and x's value is unspecified
x = std::move(x);
// x's value is still unspecified
x = std::move(temp);
// x's value now has the value of temp, which is also the original x value

要完成此 no-op,x 上的 self-move-assignment 可以做任何它想做的事情,只要它使 x 处于有效状态而不断言或抛出异常.

如果你想为你的类型指定 T self-move-assignment 是 no-op,那完全没问题。 std::lib 完全符合 unique_ptr.

如果您想为您的类型指定 U self-move-assignment 使其处于有效但未指定的状态,那也可以。 std::lib 与 vector 完全相同。一些实现(我相信 VS)会在 vector 上制造 self-move-assignment 一个 no-op。其他的不要(比如libc++)。