C ++中的标准“map”容器允许您插入rvalue:
T x;
std::map<int, T> m;
// m[1]; // populate "1"
auto it = m.insert(std::make_pair(1, std::move(x)));
问题是当元素已经存在时会发生什么,即 it->second == false
。有元素 x
已经被“搬走了”?例如,如果它是一个唯一的指针,将会 x
已被重置为null?
很明显,上述情况的答案是“是”,因为移动发生在创建对的位置。但是现在假设我想更新现有值,但仍然保留值是否已经存在的信息(所以我不能只说 m[1] = std::move(x);
)。在这种情况下,是否有可能“不要离开”物体?
我在GCC中发现以下工作[更新:适用于GCC 4.6,确实如此 不 在GCC 4.8中工作]:
auto it = m.insert(std::pair<const int, T &&>(1, std::move(x)));
但这是否保证不动?
虽然 std::move
实际上并没有执行任何动作,也没有 std::make_pair
, std::make_pair
将其论点转发给 std::pair
构造函数,从这些参数初始化其两个值成员。
因此,移动是在此之前执行的 std::map
有机会做任何事情。所以,是的,你没有充分的理由最终得到了“破碎”的举动。
你应该可以利用 emplace
(为了跳过这对结构)。从表102:
效果:插入一个 T
目的 t
用。构造 std::forward<Args>(args)...
当且仅当容器中没有元素,其键与等效键相同 t
。
显然,图书馆此时仍在“转发”,因此它是预先移动的,在你的情况下,不会发生任何安置,所以整个表达应该是一个有效的无操作。
然而, 的libstdc ++ 从 GCC 4.8.0似乎有一个bug 在这方面: emplace
所调用 _M_emplace_unique
在内部树上,将参数转发给 _M_create_node
,将参数转发给 allocator_traits<_Node_allocator>::construct
,将参数转发给 _S_construct
,将参数转发给 __a.construct
使用默认分配器,是 std::allocator<std::pair<const _Key, _Tp> >::construct
,这是你试图避免的一对构造函数...所有在碰撞检查之前 _M_emplace_unique
。
可以说这个标准在这方面是模棱两可的,但我称之为违背意图。然后, clang v3.4 with 的libc ++ 也表现出这种行为和Visual Studio 2012一样。如果我的标准解释是正确的,那么所有三个主流工具链都会失败。
我猜他们都决定将“if if only only”应用于插入,而不是插入 和 那个工程。
我已经发布了一个问题 STD-讨论 旨在激发对表102的通过的改进,以便一劳永逸地权威地回答这个问题。