Назначение в C++ происходит несмотря на исключение на правой стороне
У меня есть некоторый (C++ 14) код, который выглядит следующим образом:
map<int, set<string>> junk;
for (int id : GenerateIds()) {
try {
set<string> stuff = GetStuff();
junk[id] = stuff;
} catch (const StuffException& e) {
...
}
}
Это работает. Иногда GetStuff()
генерирует исключение, которое работает нормально, потому что, если это так, я не хочу, чтобы значение в карте мусора тогда.
Но сначала я написал это в цикле, который не работает:
junk[id] = GetStuff();
Точнее, даже когда GetStuff()
генерирует исключение, создается junk[id]
(и ему назначается пустой набор).
Это не то, что я ожидал: я ожидал, что они будут функционировать так же.
Есть ли здесь какой-то принцип C++, который я неправильно понял?
Ответы
Ответ 1
До С++ 17 не было последовательности между left- и правой частью операторов присваивания.
Впервые в С++ 17 было введено явное секвенирование (сначала вычисляется правая часть).
Это означает, что порядок оценки не определен, что означает, что до реализации следует выполнить оценку в том порядке, в котором он хочет, и в этом случае он сначала оценивает сторону left-.
См. Эту ссылку порядка оценки для более подробной информации (особенно пункт 20).
Ответ 2
станд :: Карта :: Оператор []
Возвращает ссылку на значение, которое сопоставлено с ключом, эквивалентным ключу, выполняя вставку, если такой ключ еще не существует.
junk[id]
вызывает вышеупомянутую вставку, и после того, как это уже произошло, GetStuff()
бросает. Обратите внимание, что в С++ 14 порядок, в котором это происходит, определяется реализацией, поэтому с другим компилятором your junk[id] = GetStuff();
может не выполнить вставку, если GetStuff()
.
Ответ 3
Вы не понимаете, как operator[]
работает на std::map
.
Возвращает ссылку на сопоставленный элемент. Поэтому ваш код сначала вставляет элемент по умолчанию в эту позицию, а затем вызывает operator=
для установки нового значения.
Чтобы это работало так, как вы ожидаете, вам нужно использовать std::map::insert
(*):
junk.insert(std::make_pair(id, GetStuff()));
Предостережение: insert
добавит значение только в том случае, если id
еще не сопоставлен.