Я щойно втратив три дні свого життя, відслідковуючи дуже дивну помилку, де unordered_map :: insert () знищує змінну, яку ви вставляєте. Ця надзвичайно очевидна поведінка зустрічається лише в останніх компіляторах: я виявив, що кланг 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/, де перевантаження немає 2:
template <class P> pair<iterator,bool> insert ( P&& val );
Що є жадібним універсальним контрольним перевантаженням, що споживає все, що не відповідає жодному іншому перевантаженню, і переходить, будуючи його на тип value_type. То чому ж наш код вище обрав це перевантаження, а не не упорядкований_мап :: value_type перевантаження, як, мабуть, більшість очікував?
Відповідь дивиться вам в обличчя: unordered_map :: value_type є пара < const int, std :: shared_ptr>, і компілятор правильно вважає, що пара < int , std :: shared_ptr> не конвертована. Тому компілятор вибирає універсальну контрольну перевантаження переміщення, і це знищує оригінал, незважаючи на те, що програміст не використовує std :: move (), що є типовим умовою для вказівки на те, що ви добре з змінною, що знищується. Тому поведінка руйнування вставок насправді правильна відповідно до стандарту C ++ 11, і старі компілятори були невірними .
Ви, напевно, тепер можете зрозуміти, чому мені знадобилося три дні, щоб діагностувати цю помилку. Це було зовсім не очевидно у великій базі коду, де тип, що вставляється у unordered_map, був typedef, визначеним далеко в термінах вихідного коду, і ніколи нікому не приходило в голову перевіряти, чи typedef був ідентичним value_type.
Тож мої запитання до Stack Overflow:
Чому старші компілятори не знищують змінні, вставлені як новіші компілятори? Я маю на увазі, навіть GCC 4.7 цього не робить, і це досить відповідні стандарти.
Ця проблема широко відома, оскільки, безумовно, оновлення компіляторів призведе до того, що код, який раніше працював, раптово припинить роботу?
Чи мав намір комітет зі стандартів C ++ мати таку поведінку?
Як би ви запропонували змінити unordered_map :: insert (), щоб покращити поведінку? Я запитую це тому, що якщо тут є підтримка, я маю намір подати цю поведінку як N примітку до WG21 і попросити їх реалізувати кращу поведінку.
4.9.0 20131223 (experimental)
відповідно. Результат a.second is now 0x2074088
для мене (або подібний).
a
ні. Слід зробити копію. Також ця поведінка повністю залежить від stdlib, а не від компілятора.