#новичкам
Не так редко можно увидеть код примерно такого вида:
if (map_.count(token)) {
map[token]++;
} else {
map[token] = 1;
}Типа если в мапе нет ключа, то создаем там объект со значением 1, если есть, то просто инкрементируем значение.
Не знаю, почему многие боятся написать просто вот так:
map[token]++;
Наверное не знают пары секретиков. Сегодня я о них поведаю.
Первый - оператор[] для мапы создает объект, если его не было в мапе. То есть неважно, был ли ключ в мапе или нет, вы можете вызвать operator[] и ничего плохого не произойдет. Просто в словаре будет еще один ключ, если до этого не было.
Посмотрим на пример:
struct CLASS {
CLASS() {std::cout << "default" << std::endl; i = 0;}
void operator++(int) {std::cout << "increment" << std::endl; i++; }
int i;
};
int main() {
std::map<std::string, CLASS> map;
map["qwe"]++;
std::cout << map["qwe"].i << std::endl;
}Простая оберточка, чтобы показать все наглядно. Создаем пустую мапу и инкрементируем значение по ключу, которого в ней нет. И выводим значение после инкремента.
Вывод:
default
increment
1
То есть для значения нового элемента мапы вызывается конструктор по умолчанию. Объект создается налету.
Одно беспокойство убрали.
"Но тут в примере в дефолтном конструкторе вы явно инициализируете поле i в ноль. Почему реальное интовое значение в мапе будет выставляться в ноль?"
Да. Потому что гарантируется value initialization при создания объектов в ассоциативных контейнерах.
Value инициализация для интовой переменной может выглядеть вот так:
int();
int{};
int object{};
new int();
new int{};
Эффект value initialization на тривиальных типах - они инициализируются нулем.
std::cout << int() << std::endl;
std::cout << int{} << std::endl;
int object{};
std::cout << object << std::endl;
std::cout << *(new int()) << std::endl;
std::cout << *(new int{}) << std::endl;
Вывод:
0
0
0
0
0
Этих двух "секретных знаний" достаточно, чтобы не бояться изменять значения в словарях даже если желаемого ключа в нем нет.
Know secrets. Stay cool.
#cppcore #STL