# `operator []` ассоциативных контейнеров Удивительное дело, но в этой заметке не будет ничего, связанного с неопределенным поведением. По крайней мере напрямую. В стандартной библиотеке C++ много неоднозначных решений. Одно из таких: объединить операцию вставки в ассоциативный контейнер и получения элемента. `operator []` для ассоциативных контейнеров, пытается вызвать конструктор по умолчанию для элемента, если не находит переданный ключ. С одной стороны это удобно: ```C++ std::map counts; for (Word c : text) { ++counts[word]; // ровно один поиск по ключу } ``` В иных языках придется постараться, чтобы записать то же самое и не допустить повторного поиска. ```Java map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1); // поиск трижды! map.put(key, map.getOrDefault(key, 0) + 1); // поиск дважды! ``` Оно, конечно, может быть, отоптимизируется JIT-компилятором... Но мы в C++ любим гарантии. С другой стороны, этот вызов конструктора, если элемент не найден, может выйти боком: ```C++ struct S { int x; explicit S (int x) : x {x} {} }; std::map m { { 1, S{2} }}; // Ok m[0] = S(5); // огромная трудночитаемая ошибка компиляции auto s = m[1]; // опять огромная трудночитаемая ошибка компиляции //------------------- struct Huge { Huge() { data.reserve(4096); } std::vector data; }; std::map m; Huge h; ... /* заполняем h */ m[0] = std::move(h); // бесполезный вызов default-конструктора, лишняя аллокация, // а потом перемещение ``` Чтобы выпутаться из этой неприятности, у ассоциативных контейнеров к C++17 (20) наплодили целую гору методов `insert_or_assign`, `try_emplace` и `insert` с непонятным для непосвященных возвращаемым значением `pair`. Всем этим добром, конечно же, пользоваться тяжело и неудобно. Про них пишут длинные статьи в блоги о том, как эффективно пользоваться поиском по контейнерам... С `operator[]`, конечно же, проще, «понятнее» и короче. Но и ловушка для невнимательных. А если еще и с мерзопакостными особенностями других объектов скрестить... ```C++ std::map options { {"max_value" : "1000"}; } .... const auto ParseInt = [](std::string s) { std::istringstream iss(s); int val; iss >> val; return val; }; const int value = ParseInt(options["min_value"]); //!перепутали !нет такого поля // value == 0. Все "ok". Счастливой отладки // operator[] вернул пустую строку // >> сфейлился и записал ноль в результат ``` Избежать неприятностей с `operator[]` для ассоциативных контейнеров можно, навесив `const`. И тогда вам этот оператор доступен не будет. И придется использовать либо `.at`, бросающий исключения. Либо всеми любимый: ```C++ if (auto it = m.find(key); it != m.end()) { // делай что хочешь с *it, it->second } ``` Все просто.