Mitsuru Kariya
Mitsuru Kariya

Reputation: 948

overload of std::unordered_map::insert

Would you teach me why both

std::unordered_map::insert(const value_type&)

and

template<class P> std::unordered_map::insert(P&&)

exist in the standard?

I think that insert(P&&) can serve as insert(const value_type&).

Upvotes: 15

Views: 2095

Answers (3)

sellibitze
sellibitze

Reputation: 28087

Both of these overloads

auto std::unordered_map::insert(const value_type&) -> ...

template<class P>
auto std::unordered_map::insert(P&&) -> ...

have their advantages and neither can fully replace the other. The first one seems like a special case of the second one since P might be deduced to be const value_type&. The nice thing about the 2nd overload is that you can avoid unnecessary copies. For example, in this case:

mymap.insert(make_pair(7,"seven"));

Here, the result of make_pair is actually a pair<int, const char*> whereas value_type might be pair<const int, string>. So, instead of creating a temporary value_type object and copying it into the container, we have the chance of directly creating the value_type object into the map by converting the argument and/or moving its members.

On the other hand, it would be nice if this worked as well:

mymap.insert({7,"seven"});

But this list is actually not an expression! The compiler can't deduce P for the second overload because of that. The first overload is still viable since you can copy-initialize a pair<const int,string> parameter with such a list.

Upvotes: 7

ecatmur
ecatmur

Reputation: 157324

The template universal reference overload was added in n1858, with rationale (for map, but the same explicitly applies to multimap):

Two of the insert signatures are new. They have been added to allow moving from rvalue types other than value_type, which are convertible to value_type. When P instantiates as an lvalue, the argument is copied into the map, else it is moved into the map (const qualifiers permitting).

(The other insert signature referred to is insert-with-hint.)

We also refer to the rationale for deque (again, explicitly referenced for other containers):

All member functions which insert (or append, prepend, etc.) a single value_type into the container are overloaded with a member function that accepts that value_type by rvalue reference so that single value_type's can be moved into the container. This not only makes working with heavy weight types much more efficient, it also allows one to insert movable but non-copyable types into the container.

It's apparent that the changes were considered principally as additions; it wasn't considered at the time that the template overload could replace the original (C++03) insert entirely. This can be seen by referring to the earlier n1771, which provides some motivation for the template overload, taking a different approach:

Note below that for map and multimap that there are two new insert overloads, both taking a pair with a non-const key_type. One can not move from a const key_type, and therefore to be able to move a key_type into the (multi)map, a pair must be used. There are overloads for both a const lvalue pair, and a non-const rvalue pair so that lvalue pair's will not be moved from.

pair<iterator, bool> insert(const value_type& x);  // CC
pair<iterator, bool> insert(const pair<key_type,mapped_type>& x);  // CC
pair<iterator, bool> insert(pair<key_type,mapped_type>&& x);

(CC is an abbreviation for CopyConstructible.)

It appears then that the template overloads were added to map and multimap without realising that they made the const value_type & overloads redundant. You might consider submitting a defect report to have the redundant overloads removed.

Upvotes: 4

Hannes M
Hannes M

Reputation: 751

the difference lies in the type of reference used. The first

std::unordered_map::insert(const value_type&)

uses a Reference (C++03) now called an lvalue Reference in (C++11). This needs to be const. C++11 introduced rvalue References P&& which need not to be const. To allow for both, two insert functions are provided.

Please see this excellent answer on StackOverflow wrt rvalue References in C++11, I hope this helps to answer your question.

What does T&& (double ampersand) mean in C++11?

As you said, it is possible to use the rvalue-overload and just pass a const lvalue ref, but - see this text from http://msdn.microsoft.com/en-us/library/dd293668.aspx

By overloading a function to take a const lvalue reference or an rvalue reference, you can write code that distinguishes between non-modifiable objects (lvalues) and modifiable temporary values (rvalues).

-Hannes

Upvotes: 2

Related Questions