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的通过的改进,以便一劳永逸地权威地回答这个问题。