Undefined поведение и последовательности точек перезагружены

Рассмотрите эту тему в следующем разделе:

Предыдущий взнос
Undefined поведение и точки последовательности

Пересмотрите это смешное и запутанное выражение (выделенные курсивом фразы взяты из вышеупомянутой темы * smile *):

i += ++i;

Мы говорим, что это вызывает поведение undefined. Я предполагаю, что, сказав это, мы неявно предполагаем, что тип i является одним из встроенных типов.

Что делать, если тип i является определяемым пользователем типом? Скажем, что его тип Index, который определен ниже в этом сообщении (см. Ниже). Будет ли он ссылаться на undefined -behavior?

Если да, то почему? Разве это не эквивалентно написанию i.operator+=(i.operator++()); или даже синтаксически проще i.add(i.inc());? Или они также ссылаются на undefined -behavior?

Если нет, почему бы и нет? В конце концов, объект i получает изменение дважды между последовательными точками последовательности. Пожалуйста, вспомните эмпирическое правило: выражение может изменять значение объекта только один раз между последовательными "точками последовательности". И если i += ++i - выражение, тогда он должен вызывать undefined -behavior. Если это так, то его эквиваленты i.operator+=(i.operator++()); и i.add(i.inc()); также должны ссылаться на undefined -behavior, что кажется неверным! (насколько я понимаю)

Или, 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;

четко определен в С++ 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;
        }
};

Ответ 1

Похоже на код

i.operator+=(i.operator ++());

Прекрасно работает в отношении точек последовательности. В разделе 1.9.17 стандарта С++ ISO говорится о точках последовательности и оценке функции:

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

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

Отличный вопрос, кстати! Мне очень нравится, как вы заставляете меня понимать все нюансы языка, который, как я уже думал, я знал (и думал, что я думал, что знаю).: -)

Ответ 2

Как говорили другие, ваш пример i += ++i работает с пользовательским типом, поскольку вы вызываете функции, а функции содержат точки последовательности.

С другой стороны, a[++i] = i не так повезло, полагая, что a - ваш основной тип массива или даже определенный пользователем. Проблема, которую вы получили здесь, состоит в том, что мы не знаем, какая часть выражения, содержащего i, оценивается в первую очередь. Может быть, оценивается ++i, передается на operator[] (или исходная версия), чтобы получить там объект, а затем значение i будет передано этому (которое после того, как я был увеличен), С другой стороны, возможно, последняя сторона сначала оценивается, сохраняется для последующего назначения, а затем оценивается часть ++i.

Ответ 3

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 );

Ответ 4

Я думаю, что он четко определен:

Из стандарта проекта С++ (n1905) §1.9/16:

"Существует также точка последовательности после копирование возвращаемого значения и перед выполнением любого выражения вне функции 13). Несколько контекстов в С++ оценка вызова функции, даже хотя никакой вызов соответствующей функции синтаксис появляется в переводе Блок. [Пример: оценка новой выражение вызывает один или несколько функции распределения и конструктора; см. 5.3.4. Для другого примера, вызов функции преобразования (12.3.2) может возникать в контексте который не отображает синтаксис вызова функции. - конец примера]. Точки последовательности в функция-вход и функция-выход (как описанные выше) являются особенностями вызовы функций как оцененные, синтаксис выражения, который вызов функции может быть."

Обратите внимание на ту часть, которую я выделил. Это означает, что после вызова функции инкремента (i.operator ++()) есть действительно точка последовательности, но перед вызовом составного назначения (i.operator+=).

Ответ 5

Хорошо. Пройдя предыдущие ответы, я снова подумал о своем собственном вопросе, в частности о том, что только Ной попытался ответить, но я не уверен в этом.

a[++i] = i;

Случай 1:

Если a - это массив встроенного типа. Тогда то, что сказал Ной, верно. То есть

a [++ i] = я не так повезло предположить что a - ваш основной тип массива, или даже определенный пользователем. Проблема вы здесь, что мы не знаем какая часть выражения содержащий i, сначала оценивается.

Итак, a[++i]=i вызывает undefined -behavior, или результат не указан. Как бы то ни было, это не определено!

PS: В приведенной выше цитате strike-through, конечно, принадлежит мне.

Случай 2:

Если a - объект пользовательского типа, который перегружает operator[], то снова есть два случая.

  • Если тип возвращаемого значения перегруженной функции operator[] является встроенным типом, то снова a[++i]=i вызывает undefined -behavior или результат не указан.
  • Но если возвращаемый тип перегруженной функции 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), поскольку в обеих ситуациях после каждого вызова функции существует точка последовательности.

Пожалуйста, поправьте меня, если я ошибаюсь.: -)