Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does map move-insertion guarantee that elements are or are not moved from?

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)));

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?

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?

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)));

But is this guaranteed to not move?

like image 793
Kerrek SB Avatar asked Dec 02 '13 12:12

Kerrek SB


People also ask

How elements are inserted in to a map?

The map::insert() is a built-in function in C++ STL which is used to insert elements with a particular key in the map container. Parameters: The function accepts a pair that consists of a key and element which is to be inserted into the map container.

Does map maintain insertion order C++?

C++ hash map and hash set which preserves the order of insertion. The ordered-map library provides a hash map and a hash set which preserve the order of insertion in a way similar to Python's OrderedDict. When iterating over the map, the values will be returned in the same order as they were inserted.

How does std:: move works?

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.

What does map at return?

The C++ function std::map::at() returns a reference to the mapped value associated with key k.


1 Answers

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.

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.

You should be able to take advantage of emplace (in order to skip the pair construction). From Table 102:

Effects: Inserts a T object t constructed with std::forward<Args>(args)... if and only if there is no element in the container with key equivalent to the key of t.

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.

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.

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.

I guess they all decided that the "if and only if" applied to the insertion, rather than the insertion and the construction.

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.

like image 120
Lightness Races in Orbit Avatar answered Oct 04 '22 18:10

Lightness Races in Orbit