Предполагается ли Комитету по стандартам C ++, что в C ++ 11 unordered_map уничтожает то, что он вставляет? [c++]


Answers

template <class P> pair<iterator,bool> insert ( P&& val );

Это жадная универсальная перегрузка ссылок, которая потребляет все, что не соответствует какой-либо другой перегрузке, и перемещает ее в значение value_type.

Это то, что некоторые люди называют универсальной ссылкой , но на самом деле является рутинной ссылкой . В вашем случае, когда аргументом является lvalue pair<int,shared_ptr<int>> типов pair<int,shared_ptr<int>> это не приведет к тому, что аргумент является ссылкой rvalue, и он не должен двигаться от него.

Итак, почему наш код выше выбирает эту перегрузку, а не перегрузку unordered_map :: value_type, как ожидалось бы, вероятно, больше всего?

Поскольку вы, как и многие другие люди раньше, неверно истолковали value_type в контейнере. value_type of *map (упорядоченный или неупорядоченный) является pair<const K, T> , которая в вашем случае представляет собой pair<const int, shared_ptr<int>> . Соответствующий тип исключает перегрузку, которую вы ожидаете:

iterator       insert(const_iterator hint, const value_type& obj);
Question

Решено: это ошибка в libstdc ++ <v4.8.2, которую GCC v4.8 и clang> = v3.2 будут использовать, если она присутствует в системе. См. Http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57619 для отчета. Спасибо Кейси и Брайану за то, что он дал правильный ответ. Найл

Оригинальный вопрос:

Я только что потерял три дня своей жизни, обнаружив очень странную ошибку, где unordered_map :: insert () уничтожает переменную, которую вы вставляете. Это крайне неочевидное поведение происходит только в самых последних компиляторах: я обнаружил, что clang 3.2-3.4 и GCC 4.8 являются единственными компиляторами, демонстрирующими эту «функцию».

Вот несколько приведенных кодов из моей основной базы кода, которая демонстрирует проблему:

#include <memory>
#include <unordered_map>
#include <iostream>

int main(void)
{
  std::unordered_map<int, std::shared_ptr<int>> map;
  auto a(std::make_pair(5, std::make_shared<int>(5)));
  std::cout << "a.second is " << a.second.get() << std::endl;
  map.insert(a); // Note we are NOT doing insert(std::move(a))
  std::cout << "a.second is now " << a.second.get() << std::endl;
  return 0;
}

Я, как, наверное, большинство программистов на C ++, ожидал, что вывод будет выглядеть примерно так:

a.second is 0x8c14048
a.second is now 0x8c14048

Но с clang 3.2-3.4 и GCC 4.8 я получаю это вместо этого:

a.second is 0xe03088
a.second is now 0

Что не имеет смысла, пока вы не внимательно изучите документы для unordered_map :: insert () по адресу http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/, где перегрузка no 2:

template <class P> pair<iterator,bool> insert ( P&& val );

Это жадная универсальная перегрузка ссылок, которая потребляет все, что не соответствует какой-либо другой перегрузке, и перемещает ее в значение value_type. Итак, почему наш код выше выбирает эту перегрузку, а не перегрузку unordered_map :: value_type, как ожидалось бы, вероятно, больше всего?

Ответ смотрит вам в лицо: unordered_map :: value_type - это пара < const int, std :: shared_ptr>, и компилятор правильно подумает, что пара < int , std :: shared_ptr> не конвертируется. Поэтому компилятор выбирает универсальную ссылочную перегрузку, и это разрушает оригинал, несмотря на то, что программист не использует std :: move (), что является типичным соглашением для указания, что вы в порядке с уничтоженной переменной. Поэтому поведение разрушения вставки на самом деле правильное в соответствии со стандартом C ++ 11, а старые компиляторы неверны .

Вероятно, теперь вы можете увидеть, почему я потратил три дня на диагностику этой ошибки. Это было совершенно не очевидно в большой базе кода, где тип, который был вставлен в unordered_map, был typedef, определенный далеко в терминах исходного кода, и никому не приходило в голову проверить, идентичен ли typedef значению__type.

Поэтому мои вопросы для переполнения стека:

  1. Почему старые компиляторы не уничтожают переменные, вставленные как новые компиляторы? Я имею в виду, что даже GCC 4.7 этого не делает, и это соответствует стандартам.

  2. Является ли эта проблема широко известной, поскольку, безусловно, обновление компиляторов приведет к тому, что код, который раньше работал, внезапно прекратил работу?

  3. Был ли комитет по стандартам C ++ навязывать это поведение?

  4. Как бы вы предложили изменить unordered_map :: insert (), чтобы улучшить поведение? Я спрашиваю об этом, потому что, если есть поддержка здесь, я намерен представить это поведение в качестве примечания N к РГ21 и попросить их реализовать лучшее поведение.