map move-insertion是否保证元素是否被移动?

时间:2013-12-02 12:21:42

标签: c++ c++11 move-semantics

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是否已重置为空?

上述案例中的答案显然是“是”,因为在创建配对时已经发生了移动。但是现在假设我想更新现有值,但仍然保留值是否已经存在的信息(所以我不能只说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)));

但是这可以保证不动吗?

1 个答案:

答案 0 :(得分:16)

虽然std::move实际上并没有执行任何移动,std::make_pair也没有,std::make_pair会将其参数转发给std::pair构造函数,该构造函数将其两个值成员初始化为参数。

因此,此移动是在std::map有机会做任何事情之前执行的。所以,是的,你最终没有充分的理由“破碎”了。

您应该能够利用emplace(为了跳过配对构造)。从表102:

  

效果:当且仅当容器中没有与T键相同的键的元素时,插入t对象std::forward<Args>(args)...构造t。 / p>

显然,图书馆此时仍在“转发”,所以它是预先移动的,在你的情况下,不会发生任何安置,所以整个表达应该是一个有效的无操作。

然而,来自GCC 4.8.0 appears to have a bug libstdc ++ emplace调用_M_emplace_unique on the internal tree,将参数转发给{{3}将参数转发给_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++ exhibits this behaviour too和Visual Studio 2012一样。所以如果我的标准解释是正确的,那么所有三个主流工具链都会失败。

我猜他们都决定将“if and only if”应用于插入,而不是插入构造。

我在std-discussion上发布了一个问题,旨在激发对表格102的通过的改进,以便一劳永逸地权威地回答这个问题。