Перегрузка std:: unordered_map:: insert

Не могли бы вы научить меня, почему оба

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

и

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

существуют в стандарте?

Я думаю, что insert(P&&) может служить insert(const value_type&).

Ответы

Ответ 1

Обе эти перегрузки

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

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

имеют свои преимущества, и ни одна из них не может полностью заменить другую. Первый из них кажется частным случаем второго, так как P может быть выведено как const value_type&. Хорошая вещь о второй перегрузке заключается в том, что вы можете избежать ненужных копий. Например, в этом случае:

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

Здесь результат make_pair на самом деле равен pair<int, const char*>, тогда как value_type может быть pair<const int, string>. Таким образом, вместо создания временного объекта value_type и копирования его в контейнер у нас есть возможность прямого создания объекта value_type в карте путем преобразования аргумента и/или перемещения его элементов.

С другой стороны, было бы неплохо, если бы это сработало:

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

Но этот список на самом деле не является выражением! Из-за этого компилятор не может вывести P для второй перегрузки. Первая перегрузка по-прежнему жизнеспособна, так как вы можете скопировать-инициализировать параметр pair<const int,string> с таким списком.

Ответ 2

Универсальная эталонная перегрузка шаблона была добавлена ​​в n1858 с обоснованием (для map, но то же самое явно относится к multimap):

Две из подписей insert новы. Они были добавлены, чтобы позволить перемещаться из типов r, отличных от value_type, которые можно конвертировать в value_type. Когда P создает экземпляр как lvalue, аргумент копируется в map, иначе он перемещается в map (допускают атрибуты констант).

(Другая подпись insert, на которую ссылается, - insert-with-hint.)

Мы также ссылаемся на обоснование для deque (опять же, явно ссылающееся на другие контейнеры):

Все функции-члены, которые вставляют (или добавляют, добавляют и т.д.) один объект value_type в контейнер, перегружаются с помощью функции-члена, которая принимает этот value_type по ссылке rvalue, так что единственный объект value_type может быть перемещен в контейнер. Это не только значительно повышает эффективность работы с тяжелыми весами, но и позволяет вставлять в контейнер подвижные, но не скопируемые типы.

Очевидно, что изменения рассматривались главным образом как дополнения; в то время не считалось, что перегрузка шаблона может полностью заменить исходный (С++ 03) insert. Это можно увидеть, обратившись к предыдущему n1771, который обеспечивает некоторую мотивацию для перегрузки шаблона, используя другой подход:

Обратите внимание на то, что для карты и мультимапа есть две новые перегрузки вставки, причем они берут пару с неконстантным ключом. Нельзя переходить из const key_type и, следовательно, чтобы иметь возможность перемещать key_type на карту (multi), необходимо использовать пару. Существуют перегрузки как пары const lvalue, так и пары non-const rvalue, так что пара lvalue не будет перемещена из.

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 является аббревиатурой для CopyConstructible.)

Затем появляется, что перегрузки template были добавлены к map и multimap, не осознавая, что они сделали избыточные перегрузки const value_type & избыточными. Вы можете рассмотреть возможность отправки отчета о дефектах, чтобы удалить избыточные перегрузки.

Ответ 3

разница заключается в типе используемой ссылки. Первый

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

использует ссылку Reference (С++ 03), которая теперь называется ссылкой lvalue в (С++ 11). Это должно быть const. С++ 11 представил rvalue Ссылки P&&, которые не должны быть const. Для обеспечения обеих функций предусмотрены две функции вставки.

Пожалуйста, ознакомьтесь с этим отличным ответом на StackOverflow wrt rvalue Ссылки в С++ 11, надеюсь, это поможет ответить на ваш вопрос.

Что делает T && (double ampersand) означает в С++ 11?

Как вы сказали, можно использовать rvalue-overload и просто передать константу lvalue ref, но - см. этот текст из http://msdn.microsoft.com/en-us/library/dd293668.aspx

Перегружая функцию для получения ссылки на константу lvalue или ссылки на rvalue, вы можете написать код, который отличает немодифицируемые объекты (lvalues) и изменяемые временные значения (rvalues).

-Hannes