关于移动分配的标准库政策是the implementation is allowed to assume that self-assignment will never happen;在我看来这是一个非常糟糕的主意,因为:
remove_if
家庭中的任何事情都需要照顾这个角落; 那么,为什么要做出这样的决定?
¹特别是在库代码中,实现者可以自由地利用关于“分支预期结果”的编译器特定提示(在VC ++中考虑gcc / __builtin_expect
中的__assume
)。
答案 0 :(得分:5)
从std
中移出的对象应该被丢弃或在重用之前分配给它们。 任何未完全免费的都不会被承诺。
有时事情是免费的。就像一个移动构造的容器是空的。请注意,某些移动辅助的情况没有这样的保证,因为某些实现可能会选择移动元素而不是缓冲区。为什么不同?一个是免费的额外保证,另一个不是。
分支机构或其他支票并非完全免费。它占用了一个分支预测槽,即使预测它几乎是免费的。
最重要的是,a = std::move(a);
是逻辑错误的证据。分配来自a
(std
内)意味着您只会分配或放弃a
。然而,在这里,你希望它在下一行有特定的状态。要么你知道你是自我分配,要么你不知道。如果你不这样做,你现在正在从你正在填充的对象移动,而你却不知道它。
“做一件小事以保证安全”的原则与“你不为你不使用的东西付钱”相冲突。在这种情况下,第二个赢了。
答案 1 :(得分:4)
Yakk gives a very good answer(像往常一样投票)但这次我想补充一点信息。
过去五年来,自动转让政策略有改变。我们最近在LWG 2468澄清了这个角落案例。实际上,我应该更准确一点:会议之间的非正式小组同意解决这个问题,并且可能会在下个月(2016年11月)投票进入C ++ 1z工作草案。
问题的要点是修改MoveAssignable
要求,以澄清如果移动赋值的目标和来源是同一个对象,那么在赋值后对对象的值没有要求(除了它必须是有效的状态)。它进一步阐明了如果这个对象与std :: lib一起使用,它仍然必须满足算法的要求(例如LessThanComparable
),无论它是移动分配的还是甚至是自我分配的。
因此...
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
但x
和y
仍处于有效状态。没有记忆被泄露。没有发生未定义的行为。
此职位的基本原理
它仍然是表现。然而,人们认识到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)
是(相当昂贵的)无操作(使用副本而不是移动)。 LWG 2468澄清了使用C ++ 11及之后,swap(x, x)
仍然是一个(不太昂贵)的无操作(使用move而不是copy)。
详细说明:
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
要完成此操作,x
上的无操作,自动移动分配可以执行任何需要的操作,只要它使x
处于有效状态而不断言或抛出异常。
如果您想为您的类型T
指定自动分配是无操作,那就完全没问题了。 std :: lib完全与unique_ptr
完全相同。
如果要为类型U
指定自动移动分配使其处于有效但未指定的状态,那也没关系。 std :: lib完全与vector
完全相同。一些实现(我相信VS)在vector
无操作上进行自动移动分配。其他的不(如libc ++)。