Определено ли это поведение кода?
Что выводит следующий код на консоль?
map<int,int> m;
m[0] = m.size();
printf("%d", m[0]);
Возможные ответы:
- Поведение кода не определено, поскольку он не определен, какой оператор
m[0]
или m.size()
выполняется сначала компилятором. Поэтому он может печатать 1
, а также 0
.
- Он печатает
0
, потому что выполняется правая часть оператора присваивания.
-
Он печатает 1
, потому что operator[]
имеет наивысший приоритет для полного оператора m[0] = m.size()
. Из-за этого происходит следующая последовательность событий:
-
m[0]
создает новый элемент на карте
-
m.size()
получает вызов, который теперь 1
-
m[0]
получает назначенное ранее возвращенное (по m.size()) 1
-
Настоящий ответ?, который мне неизвестен ^^
Ответы
Ответ 1
Я считаю, что неопределенно, сохраняется ли 0 или 1 в m[0]
, но это не поведение undefined.
LHS и RHS могут встречаться в любом порядке, но оба они являются вызовами функций, поэтому они оба имеют точку последовательности в начале и конце. Нет никакой опасности, что двое из них, коллективно, обращаются к одному и тому же объекту без промежуточной точки последовательности.
Назначение - это фактическое присвоение int, а не вызов функции с соответствующими точками последовательности, поскольку operator[]
возвращает T&
. Это короткое беспокойство, но это не изменение объекта, к которому обращаются в любом другом месте этого заявления, так что это тоже безопасно. Он получил доступ к operator[]
, конечно, там, где он инициализирован, но это происходит перед точкой последовательности при возврате из operator[]
, так что ОК. Если это не так, m[0] = 0;
тоже будет undefined!
Однако порядок оценки операндов operator=
не указан стандартом, поэтому фактический результат вызова size()
может быть 0 или 1 в зависимости от того, какой порядок выполняется.
Однако было бы undefined поведение. Он не вызывает вызовы функций и поэтому не позволяет предотвратить доступ к size
(по RHS) и изменен (на LHS) без промежуточной точки последовательности:
int values[1];
int size = 0;
(++size, values[0] = 0) = size;
/* fake m[0] */ /* fake m.size() */
Ответ 2
В этом выражении нет последовательность точек между вызовом operator []
и вызовом clear
. Следовательно, поведение должно быть undefined.
Ответ 3
Он печатает 1 и не поднимает предупреждение (!) с помощью gcc. Он должен вызывать предупреждение, потому что это undefined.
Класс приоритета как operator[]
, так и operator.
равен 2, тогда как класс приоритета operator=
равен 16.
Это означает, что он четко определен, что m[0]
и m.size()
будут выполнены до назначения. Однако не определено, какой из них выполняется первым.
Ответ 4
Учитывая, что С++ 17 здесь в значительной степени, я думаю, стоит упомянуть, что этот код теперь демонстрирует хорошо определенное поведение в соответствии с новым стандартом. Для этого случая =
является встроенным присваиванием целому числу:
[expr.ass]/1:
Оператор присваивания (=) и все операторы присваивания группа справа налево. Все требуют модификации lvalue как их левые операнд и возвращает lvalue, ссылаясь на левый операнд. Результат во всех случаях это бит-поле, если левый операнд является битовым полем. В целом случаев, присваивание упорядочивается после вычисления значения правый и левый операнды, а перед вычислением значения присваивание. Правильный операнд секвентирован перед левым операнд. Что касается вызова функции с неопределенной последовательностью, операция составного присвоения представляет собой единую оценку.
Что оставляет нас только с одним вариантом, а это № 2.