Неопределенное поведение и последовательность точек перезагружены - PullRequest
82 голосов
/ 09 января 2011

Считайте эту тему продолжением следующей темы:

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

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

i += ++i;

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

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

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

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

Или i += ++i не является выражением для начала? Если так, то что это такое и каково определение выражение ?

Если это выражение, и в то же время его поведение также четко определено, то это означает, что число точек последовательности, связанных с выражением, так или иначе зависит от типа 1055 * операндов, участвующих в выражении. Я прав (хотя бы частично)?


Кстати, а как насчет этого выражения?

//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;
        }
};

Ответы [ 5 ]

48 голосов
/ 09 января 2011

похоже на код

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

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

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

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

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

12 голосов
/ 09 января 2011

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 );
11 голосов
/ 09 января 2011

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

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

8 голосов
/ 09 января 2011

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

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

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

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

6 голосов
/ 09 января 2011

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

a[++i] = i;

Случай 1:

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

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

Так что a[++i]=i вызывает неопределенное поведение, или результат не определен.Что бы это ни было, оно не совсем определено!

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

Случай 2:

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

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

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

...