Подтвердить что ты не робот

С++: 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().

4b9b3361

Ответ 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 он, возможно, может быть оптимизирован в основном на одно и то же снова.