Считайте эту тему продолжением следующей темы:
Предыдущая партия
Неопределенное поведение и точки последовательности
Давайте еще раз вернемся к этому забавному и запутанному выражению (выделенные курсивом фразы взяты из темы выше * улыбка *):
i += ++i;
Мы говорим, что это вызывает неопределенное поведение. Я предполагаю, что, говоря это, мы неявно предполагаем, что type of i
является одним из встроенных типов.
Что делать , если тип из i
является определенный пользователем тип? Скажите, что его тип Index
определен позже в этом посте (см. Ниже). Будет ли он по-прежнему вызывать неопределенное поведение?
Если да, то почему? Разве это не эквивалентно написанию i.operator+=(i.operator++());
или даже синтаксически проще i.add(i.inc());
? Или они тоже вызывают неопределенное поведение?
Если нет, то почему? В конце концов, объект i
изменяется дважды между последовательными точками последовательности. Вспомните эмпирическое правило: выражение может изменять значение объекта только один раз между последовательными "точками последовательности" . И если i += ++i
это выражение, то оно должно вызывать неопределенное поведение. Если да, то его эквиваленты, i.operator+=(i.operator++());
а i.add(i.inc());
также должны вызывать неопределенное поведение, которое кажется неправдой! (насколько я понимаю)
Или i += ++i
это не выражение для начала? Если да, то что это такое и каково определение выражения ?
Если это выражение, и в то же время его поведение также четко определено, то это означает, что количество точек последовательности, связанных с выражением, так или иначе зависит от типа операндов, задействованных в выражении. Правильно ли я (хотя бы частично)?
Кстати, а как насчет этого выражения?
//Consider two cases:
//1. If a is an array of a built-in type
//2. If a is user-defined type which overloads the subscript operator!
a[++i] = i; //Taken from the previous topic. But here type of `i` is Index.
Вы также должны учитывать это в своем ответе (если вы точно знаете его поведение). :-)
Является
++++++i;
четко определен в C ++ 03? Ведь вот это,
((i.operator++()).operator++()).operator++();
class Index
{
int state;
public:
Index(int s) : state(s) {}
Index& operator++()
{
state++;
return *this;
}
Index& operator+=(const Index & index)
{
state+= index.state;
return *this;
}
operator int()
{
return state;
}
Index & add(const Index & index)
{
state += index.state;
return *this;
}
Index & inc()
{
state++;
return *this;
}
};
s
определяемый пользователем!)Ответы:
Похоже на код
i.operator+=(i.operator ++());
Прекрасно работает в отношении точек последовательности. В разделе 1.9.17 стандарта C ++ ISO говорится о точках последовательности и оценке функций:
Это может указывать, например, на то, что
i.operator ++()
параметр вoperator +=
качестве параметра имеет точку следования после его оценки. Короче говоря, поскольку перегруженные операторы являются функциями, применяются обычные правила последовательности.Кстати, отличный вопрос! Мне очень нравится, как вы заставляете меня понимать все нюансы языка, который я уже думал, что знаю (и думал, что думал, что знаю). :-)
источник
http://www.eelis.net/C++/analogliterals.xhtml На ум приходят аналоговые литералы
unsigned int c = ( o-----o | ! ! ! ! ! o-----o ).area; assert( c == (I-----I) * (I-------I) ); assert( ( o-----o | ! ! ! ! ! ! ! o-----o ).area == ( o---------o | ! ! ! o---------o ).area );
источник
Как говорили другие, ваш
i += ++i
пример работает с определяемым пользователем типом, поскольку вы вызываете функции, а функции содержат точки последовательности.С другой стороны,
a[++i] = i
не так повезло, если предположить, чтоa
это ваш базовый тип массива или даже определенный пользователем. Проблема в том, что мы не знаем, какая часть содержащегося выраженияi
вычисляется первой. Это может быть то, что++i
оценивается, передаетсяoperator[]
(или необработанная версия), чтобы получить там объект, а затем значениеi
передается этому (что происходит после того, как i был увеличен). С другой стороны, возможно, сначала оценивается последняя сторона, сохраняется для последующего назначения, а затем++i
оценивается часть.источник
++i
и чтениемi
на правой стороне присвоения, то порядок не был бы определен. Поскольку один из допустимых порядков выполняет эти две вещи без промежуточной точки последовательности, поведение не определено.a
и встроенныйi
.Я думаю, это четко определено:
Из проекта стандарта C ++ (n1905) §1.9 / 16:
Обратите внимание на часть, которую я выделил жирным шрифтом. Это означает, что действительно есть точка последовательности после вызова функции приращения (
i.operator ++()
), но перед вызовом составного присваивания (i.operator+=
).источник
Хорошо. Просмотрев предыдущие ответы, я заново подумал о своем собственном вопросе, особенно о той части, на которую пытался ответить только Ной, но я не полностью убежден в нем.
Случай 1:
Если
a
- это массив встроенного типа. Тогда то, что сказал Ной, верно. То есть,Таким образом,
a[++i]=i
вызывает неопределенное поведение, или результат не указан. Что бы это ни было, это не совсем точно!PS: В приведенной выше цитате
пробитиеконечно мой.Случай 2:
Если
a
это объект пользовательского типа, который перегружает объектoperator[]
, то опять же есть два случая.operator[]
функции является встроенным, то сноваa[++i]=i
вызывает неопределенное поведение или результат не указан.operator[]
функции определенный пользователем тип, то поведениеa[++i] = i
определено корректно (насколько я понимаю), так как в этом случаеa[++i]=i
эквивалентно записи ,a.operator[](++i).operator=(i);
которая является такой же , как,a[++i].operator=(i);
. То есть присваиваниеoperator=
вызывается для возвращаемого объектаa[++i]
, который кажется очень четко определенным, поскольку к моментуa[++i]
возврата++i
уже были оценены, а затем возвращаемый объект вызываетoperator=
функцию, передавая ему обновленное значениеi
в качестве аргумента. Обратите внимание, что между этими двумя вызовами есть точка последовательности . А синтаксис гарантирует отсутствие конкуренции между этими двумя вызовами, иoperator[]
будет вызван первым, и последовательно++i
переданный ему аргумент также будет сначала оценен.Думайте об этом как о том,
someInstance.Fun(++k).Gun(10).Sun(k).Tun();
что каждый последующий вызов функции возвращает объект определенного пользователем типа. Мне эта ситуация кажется больше такой:,eat(++k);drink(10);sleep(k)
потому что в обеих ситуациях существует точка последовательности после каждого вызова функции.Пожалуйста, поправьте меня, если я ошибаюсь. :-)
источник
k++
иk
которые не разделены точками последовательности. Оба они могут быть оценены перед темSun
илиFun
иным. Язык требует только то, чтоFun
вычисляется раньшеSun
, а не то, чтоFun
аргументы вычисляются передSun
аргументами. Я как бы снова объясняю то же самое, не имея возможности дать ссылку, поэтому мы не собираемся двигаться дальше.Sun
, ноFun
аргумент++k
может быть вычислен до или после этого. Есть точки последовательности до и после выполненияFun
, ноSun
аргументk
может быть вычислен до или после этого. Следовательно, один из возможных случаев состоит в том, что обаk
и++k
оцениваются до того, как либоSun
илиFun
оцениваются, и поэтому оба находятся перед точками последовательности вызова функции, и поэтому нет точки последовательности, разделяющейk
и++k
.eat(i++);drink(10);sleep(i);
? ... даже сейчас, можно сказать,i++
может быть проведена оценка до или после этого?k
и++k
. В примере есть / пить, есть в качестве точки последовательности междуi
иi++
.eat()
иsleep()
существует точка (точки) последовательности, но между ними нет ни одного аргумента. Как могут аргументы двух вызовов функций, разделенных точками последовательности, принадлежать одним и тем же точкам последовательности?