15

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&).

5
  • Legacy from TR1 possibly. Maybe they wanted to be safe and not remove functions, only add them. Perhaps they weren't certain at the time of the impact it might have on existing code that used TR1. Commented Feb 6, 2013 at 4:58
  • I tried to construct a program that fails to compile if you remove insert(const value_type&), and failed. Anyone have thoughts? Commented Feb 6, 2013 at 5:13
  • std::vector<int> v1; v1.push_back(4); insert(v1.back()); seems to not be compiling for example. I expect whenever the value is constant and not movable the const ref method is used. Commented Feb 6, 2013 at 8:27
  • @typical "the const ref method is used" - Ah, so you mean the method where P is deduced as const value_type& and P&& collapses to const value_type&? Oh wait... Commented Feb 6, 2013 at 10:25
  • No, sir. I am saying that inserting vector.back() value call without insert function taking const ref my example did not compile with error: main.cpp:349:22: error: no matching function for call to ‘insert(__gnu_cxx::__alloc_traits<std::allocator<int> >::value_type&, std::vector<int>&)’ Commented Feb 7, 2013 at 7:46

3 Answers 3

7

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot!! Since I cannot understand completely yet why insert(const value_type&) is viable for insert({7,"seven"}), I read the standard more. Thanks again!!
I posted a related question.
4

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.

1 Comment

Thank you for the answer. However, I think that the form insert({...})(in sellibitze's answer) is useful too, even if a copy occurs.
2

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

2 Comments

oops, sorry for the repetition of the link. I started to write my answer when there was no answer yet, but got interrupted.
Instead of just reading the first part (about move semantics) of both linked posts, you should probably also read the second parts regarding perfect forwarding and the reference collapsing rules. Since insert will either copy or move the passed argument there is no reason to distinguish between lvalues and rvalues in insert itself, value_type's constructor will do that for you.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.