地图移动插入是否保证元素被移动或不移动? [英] Does map move-insertion guarantee that elements are or are not moved from?
问题描述
C ++中的标准地图容器允许您插入右值:
The standard "map" containers in C++ allow you to insert an 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?
The question is what happens when the element already exists, i.e. it->second == false
. Has the element x
already been "moved-from"? For instance, if it is a unique pointer, will x
have been reset to null?
上述情况是是,因为移动从已经在创建对的点发生。但是现在我想更新现有的值,但仍然保留该值是否已经存在的信息(所以我不能只说 m [1] = std :: move(x) ;
)。
Patently the answer in the above case is "yes", because the moving-from happens already at the point of creating the pair. But suppose now I want to update the existing value, but still retain the information of whether the value already existed or not (so I can't just say m[1] = std::move(x);
). Is it possible to "not move from" the object in that case?
我在GCC发现以下工作[更新:在GCC 4.6中工作,不是在GCC 4.8中工作]:
I discovered in GCC that the following works [Update: works in GCC 4.6, does not work in 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
构造函数
Though std::move
does not actually perform any move, and neither does std::make_pair
, std::make_pair
forwards its arguments to the std::pair
constructor, which initialises its two value members from those arguments.
这样,在 std :: map $之前的那一刻执行移动c $ c>有机会做任何事情。
As such, the move is performed at that point, before the std::map
has a chance to do anything. So, yes, you end up with a "broken" move for no good reason.
你应该能够利用 emplace
You should be able to take advantage of emplace
(in order to skip the pair construction). From Table 102:
效果:插入
T
对象如果且仅当容器中没有带键的元素时,用
std :: forward< Args>(args)...
t 的键。
Effects: Inserts a
T
objectt
constructed withstd::forward<Args>(args)...
if and only if there is no element in the container with key equivalent to the key oft
.
很明显,转发在这一点上,所以它的预移动,在你的情况下,不会发生,所以整个表达式应该是一个有效的无效操作。
Clearly, the library is still "forwarding" at this point so it's pre-move, and in your case no emplace shall take place so the entire expression ought to be an effective no-op.
但, libstdc ++ 来自 GCC 4.8.0似乎有错误在这方面: 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
中的碰撞检查之前。
However, libstdc++ from GCC 4.8.0 appears to have a bug in this regard: emplace
invokes _M_emplace_unique
on the internal tree, which forwards the arguments to _M_create_node
, which forwards the arguments to allocator_traits<_Node_allocator>::construct
, which forwards the arguments to _S_construct
, which forwards the arguments to __a.construct
which, with the default allocator, is std::allocator<std::pair<const _Key, _Tp> >::construct
, which is the pair constructor you were trying to avoid... all before the collision check in _M_emplace_unique
.
可以声称这个标准在这方面是不明确的,但我认为它违反了意图。然后, clang v3.4与 libc ++ 也显示此行为,如果我的标准解释是正确的,这在所有三个主流工具链上都会失败。
It could be claimed that the standard is ambiguous in this regard, but I'd call it a violation of intent. Then again, clang v3.4 with libc++ exhibits this behaviour too, as does Visual Studio 2012. So if my standard interpretation is correct, this fails on all three of the mainstream toolchains.
我猜他们都决定应用if and only if
I guess they all decided that the "if and only if" applied to the insertion, rather than the insertion and the construction.
我已经在 std-discussion 旨在激发对Table 102授权一次性地回答这个问题。
I have posted a question on std-discussion aiming to provoke an improvement to the passage from Table 102 to authoritatively answer this once and for all.
这篇关于地图移动插入是否保证元素被移动或不移动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!