С++: value_type против make_pair, что быстрее для вставки карты?
typedef map<KeyType, ValType> KVMap;
KVMap kvmap;
kvmap.insert( KVMap::value_type( key, val ) );
kvmap.insert( make_pair( key, val ) );
Какой из перечисленных выше вариантов вставить на карту STL всегда быстрее? Почему?
Примечание. Мне хорошо известно, что insert()
быстрее, чем использование []=
для добавления (не обновления) пар ключ-значение к карте. Предположим, что мой запрос касается добавления, а не обновления. Поэтому я ограничил его insert()
.
Ответы
Ответ 1
Скорее всего, из-за этого первый будет "эпсилон быстрее" (из 23.3.1 в стандарте):
typedef pair<const Key, T> value_type;
[...]
pair<iterator, bool> insert(const value_type& x);
-
В первой версии вы непосредственно строите подходящий тип, ожидаемый std::map<K,V>::insert
-
Во второй версии используется преобразование с использованием конструктора шаблона std::pair
. Действительно, std::make_pair
, скорее всего, выведет его аргументы шаблона в KeyType
и ValType
, возвращая таким образом std::pair<KeyType, ValType>
.
Это не соответствует типу параметра std::map<K,V>::insert
, который равен std::pair<const KeyType, ValType>
(разница в const
-qualified first). Конструктор преобразования std::pair
будет использоваться для создания std::pair<const K, V>
из std::pair<K, V>
.
Чтобы быть справедливым, я не верю, что вы даже можете измерить разницу (и я даже не уверен, что популярные компиляторы будут генерировать для них другой код).
Ответ 2
На самом деле есть аргумент для value_type
over make_pair
. Это связано с тем, что по различным тайным причинам make_pair
принимает свои аргументы по значению. С другой стороны, value_type
, псевдоним для std::pair<const Key, value>
, будет иметь свой конструктор, вызванный с аргументами, переданными с помощью ссылки const. Там потенциальная потеря эффективности от пропущенного значения в make_pair
по сравнению с передачей по ссылке, что теоретически может оказать заметное влияние на вашу программу.
Еще одна проблема, беспокоящаяся с помощью make_pair
, заключается в том, что make_pair
обычно создает пару типа std::pair<Key, Value>
по сравнению с std::pair<const Key, value>
, необходимым внутри map
. Это означает, что может быть сделана другая ненужная копия, на этот раз pair
, чтобы преобразование работало правильно.
Короче говоря, использование make_pair
может привести к двум совершенно ненужным копиям ключа и значения, которые будут сделаны, а использование конструктора value_type
не имеет.
Ответ 3
Это просто дополнение.
insert( make_pair(...) )
вызывает конструктор копирования 4 раза по умолчанию по причине другой
упомянутые респонденты.
insert( value_type(...) )
вызывает конструктор копирования 2 раза.
operator[]
вызывает конструктор по умолчанию один раз и копирует конструктор 2 раза
в типичной реализации.
конструктор по умолчанию вызывается внутри operator[]
для
insert( value_type( ..., mapped_type() ) )
.
copy-конструктор вызывается один раз для копирования аргумента insert()
(pair
),
и один раз для копирования-построения внутреннего node карты.
Итак, если вы используете insert
с make_pair
,
нельзя сказать, что insert
всегда быстрее, чем operator[]
даже для добавления.
Наверное, это зависит от ситуации.
Как вы, возможно, знаете, с учетом вышеизложенного было предложено emplace
для нового
стандарт.
Ответ 4
Они принципиально одно и то же. KVMap::value_type
является typedef для std::pair<KeyType, ValType>
, поэтому просто вызывает конструктор. std::make_pair
- это функция шаблона, которая просто вызывает конструктор (существует, потому что типы шаблонов могут быть выведены для бесплатных функций, но не для конструкторов). Как только все невероятно-стандартные оптимизации будут выполнены, нет никаких оснований для каких-либо различий.
Я не знаю, как вы тестируете, но есть много способов сделать это неправильно.
Что касается insert()
и назначения через operator[]
, последний должен сделать больше работы концептуально (когда вы добавляете новый элемент таким образом, сначала он должен по умолчанию создать элемент, а затем назначить поверх него из этого), но в зависимости от ValType
он, возможно, может быть оптимизирован в основном на одно и то же снова.