std::map insert && перегрузка вызывает копирование

Глядя на этот интересный разговор:

CppCon 2017: Мэтт Кулукундис "Создание быстрой, эффективной и удобной для кэширования хэш-таблицы, шаг за шагом"

Около минуты 38:32 он упоминает, что

void Benchmark_Slow(int iters) {
    std::unordered_map<string, int> m;
    std::pair<const string, int> p = {};
    while (iters--) m.insert(p)
}

примерно в 2 раза медленнее, чем следующий вариант

void Benchmark_Fast(int iters) {
    std::unordered_map<string, int> m;
    const std::pair<const string, int> p = {};
    while (iters--) m.insert(p)
}

Я все еще думаю о том, почему && перегрузка (1) будет выбрана.

  • std::pair<iterator,bool> insert( value_type&& value ); (1)

  • std::pair<iterator,bool> insert( const value_type& value ); (3)

где value_typeявляется std::pair<const Key, T>,

Ведь мы не двигаем ценности, поэтому в моем понимании выражение p должно быть lvalue, а не x/prvalue, верно? Может ли кто-нибудь просветить меня?

2 ответа

Решение

Вы не принимаете проблемные перегрузки:

std::pair<iterator,bool> insert(const value_type& value); // (1)

template< class P >
std::pair<iterator,bool> insert(P&& value); // (2)

P выводится как value_type&,

  1. Перегрузка шаблона вызывается ( overload (2) для cppreference)
  2. Перегрузка шаблона эквивалентна emplace
  3. emplace не медленнее, что insert и скорее в общем случае, но...

Но, emplace допускает одно злоупотребление, и это когда вы неоднократно пытаетесь вставить один и тот же ключ. И тест делает именно это (обратите внимание, что while). Я бы сказал, что это просто эталон, показывающий поведение, когда вы намеренно стреляете себе в ногу. В реальном мире я не думаю, что можно было бы сделать это через emplace или же insert,

В C++17 это было исправлено одним способом, который требует изменения вашего кода:

  1. try_emplace функция https://isocpp.org/files/papers/n4279.html
Другие вопросы по тегам