Безопасно использовать оператор + = для создания новой записи std:: map с помощью []?

Скажем, у меня есть std::map<int, int>, было бы безопасно это сделать?

std::map<int, int> m_map;
m_map[0] += 1;

Если ключ 0 не существовал на карте, когда я это делаю, как бы он знал, какое значение добавить 1 в?

Я надеюсь, что std:: map справится с этим, выполнив = вместо += в том случае, когда значение создает новую запись на карте. Это избавило бы меня от необходимости:

std::map<int, int>::iterator it = m_map.find(0);
if(it != m_map.end()) {
    it->second += 1;
}
else {
    m_map[0] = 1;
}

Ответ 1

Элемент, вставленный в карту при вызове operator[] из-за ранее неименованного ключа, является значением-инициализированным. Если вы не видели этот синтаксис, рассмотрите особое значение () в следующем фрагменте кода. Парны важны. Они вводят другое отклонение от дерева инициализации, чем инициализация по умолчанию. Оба важны; оба изложены языковым стандартом.

int i = int();

Как оказалось, инициализация значения для скаляров (включая указатели) в конечном итоге поддается нулевой инициализации. Хотя нечетное выражение, предыдущее значение фрагмента, инициализирует экземпляр int, который становится нулевой инициализацией, так как int является скаляром, а затем копирует его на i. (Чтобы быть справедливым, почти наверняка будет некоторое ускорение, но основы представлены как).

Несмотря на это, благодаря этой функции вы можете быть уверены, когда вы это сделаете:

m_map[0] += 1;

или даже это:

++m_map[0];

если индекс не был отображен ранее, добавлен элемент с инициализацией значения, который будет инициализирован нулевым значением для скаляров, что означает, что вы официально начнете с нуля.

Стоит упомянуть, что аналогичная деятельность происходит для любого типа с неявным объявлением конструктора. Тривиально или нет, происходит что-то интересное.

struct S { int a; int b; };
std::map<int, S> mymap;

++mymap[0].a;

Является ли элемент a сопоставлен с 0 в нашем контейнере надежно 1 после выполнения вышеперечисленного? Да, это так. Далее рассмотрим следующее:

struct S { int a; std::string str; };
std::map<int, S> mymap;

++mymap[0].a;

Теперь S имеет нетривиальный неявный конструктор (он должен, как он должен построить str). Но член a отображается на 0 в нашем контейнере, который все еще надежно инициализируется нулем (и, следовательно, 1 после указанной строки)? Да, это так.

Если вас интересуют ссылки на разные пути инициализации, см. этот вопрос и ответ. Или просмотрите стандарт С++ 11, особенно С++ 11 § 8.5 Инициализаторы, (p5, p7, p10). Это стоит прочитать.

Ответ 2

Это гарантирует, что вы получите 1 даже с более коротким фрагментом.

Ключ в том, что operator[] должен создать элемент на карте перед возвратом ссылки на него, поэтому к тому времени, когда вы добрались до +=, элемент уже существует и, если он должен был быть создан сейчас, с нулевым значением (поскольку элементы карты инициализируются значением).

(кстати, именно поэтому, когда вы используете std::map с типом класса как значение, он должен иметь конструктор по умолчанию, если вы хотите использовать operator[], даже если вы назначаете ему объект немедленно)

Ответ 3

Да, это нормально, новые записи по умолчанию имеют значение value_type(), которое 0 для int.

Он все еще делает +=, но 0 += 1 дает 1.

Ответ 4

Карта лениво инициализируется конструктором по умолчанию. (ноль для всех int)

Так что он 0 не существовал на карте, он был бы инициализирован, чтобы содержать значение 0 и 1 будет добавлено после += 1.

Таким образом, вы можете использовать верхнюю версию кода без каких-либо проблем.

Цитата Эффект вызова map_obj [k] из cplusplus.com

Если k соответствует ключу элемента в контейнере, функция возвращает ссылку на его отображаемое значение.

Если k не соответствует ключу любого элемента в контейнере, функция вставляет новый элемент с этим ключом и возвращает ссылку к его отображаемому значению. Обратите внимание, что это всегда увеличивает контейнер размер на единицу, даже если для элемента не присвоено сопоставленное значение ( элемент создается с использованием его конструктора по умолчанию).