Подтвердить что ты не робот

Это поведение undefined, если несколько операндов в составном выражении изменяют один и тот же объект?

Я смутно помню, как где-то читал, что это поведение undefined, если несколько операндов в составном выражении изменяют один и тот же объект.

Я считаю, что пример этого UB показан в коде ниже, но я скомпилирован на g++, clang++ и visual studio, и все они распечатывают одни и те же значения и не могут создавать непредсказуемые значения в разных компиляторах.

#include <iostream>

int a( int& lhs ) { lhs -= 4; return lhs; }
int b( int& lhs ) { lhs *= 7; return lhs; }
int c( int& lhs ) { lhs += 1; return lhs; }
int d( int& lhs ) { lhs += 2; return lhs; }
int e( int& lhs ) { lhs *= 3; return lhs; }

int main( int argc, char **argv )
{
    int i = 100;
    int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i );

    std::cout << i << ", " << j << std::endl;

    return 0;
}

Это поведение undefined или я каким-то образом вызвал описание предполагаемого UB, который на самом деле не является undefined?

Буду признателен, если кто-нибудь сможет опубликовать пример этого UB и, возможно, даже укажет мне, где в стандарте С++ он говорит, что это UB.

4b9b3361

Ответ 1

Нет. Это не. Undefined здесь не может быть и речи (предполагая, что арифметика int не переполняется): все модификации i изолированы точками последовательности (используя терминологию С++ 03). Там есть точка последовательности на входе в каждую функцию и есть точка последовательности на выходе.

Здесь поведение не указано.

Ваш код фактически следует тому же шаблону, что и классический пример, часто используемый для иллюстрации разницы между Undefined и неуказанным поведением. Рассмотрим это

int i = 1;
int j = ++i * ++i;

Люди часто утверждают, что в этом примере "результат не зависит от порядка оценки, и поэтому j всегда должен быть 6". Это неверное требование, поскольку поведение undefined.

Однако в этом примере

int inc(int &i) { return ++i; }

int i = 1;
int j = inc(i) * inc(i);

поведение формально только неуказано. А именно, порядок оценки неуточнен. Однако, поскольку результат выражения не зависит вообще от порядка оценки, j гарантированно всегда заканчивается как 6. Это пример того, как обычно опасная неопределенная комбинация поведения может привести к совершенно определенному результату.

В вашем случае результат вашего выражения критически зависит от порядка оценки, а это значит, что результат будет непредсказуем. Однако здесь не существует поведения Undefined, то есть программе не разрешается форматировать жесткий диск. Разрешено производить непредсказуемый результат в j.

P.S. Опять же, может оказаться, что некоторые сценарии оценки для вашего выражения приводят к значению целочисленного переполнения (я их не анализировал), что само по себе вызывает поведение Undefined. Таким образом, по-прежнему существует потенциал для неопределенного поведения, приводящего к поведению Undefined в вашем выражении. Но это, вероятно, не то, о чем вы говорите.

Ответ 2

Нет его поведения не undefined.

Но он вызывает неуказанное поведение.

Это связано с тем, что порядок, в котором выражены подвыражения, не указан.

int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i );

В приведенном выше выражении вспомогательные выражения:

b(i)
c(i)
e(i)
a(i)
d(i)

Может быть оценен в любом порядке. Поскольку все они имеют побочные эффекты, результаты будут зависеть от этого порядка.

Если вы разделите выражение на все подвыражения (это псевдокод)
Затем вы можете увидеть любой заказ. Эти выражения могут не только выполняться в любом порядке, но потенциально они могут чередоваться с подвыражениями более высокого уровня (только с несколькими ограничениями).

tmp_1 = b(i)           // A
tmp_2 = c(i)           // B
tmp_3 = e(i)           // C
tmp_4 = a(i)           // D
tmp_5 = d(i)           // E

tmp_6 = tmp_1 + tmp_2  // F   (Happens after A and B)
tmp_7 = tmp_6 * tmp_3  // G   (Happens after C and F)
tmp_8 = tmp_7 / tmp_4  // H   (Happens after D and G)
tmp_9 = tmp_8 * tmp_5  // I   (Happens after E and H)

int j = tmp_9;         // J   (Happens after I)

Ответ 3

Это не поведение undefined, но оно имеет неуказанные результаты. Единственный измененный объект - i через ссылки, переданные функциям. Тем не менее, вызов функций вводит точки последовательности (у меня нет С++ 2011 со мной: они называются чем-то другим), т.е. В выражении не возникает проблемы с несколькими изменениями, вызывающими поведение undefined.

Однако порядок, в котором вычисляется выражение, не указан. В результате вы можете получить разные результаты, если порядок оценки изменится. Это не поведение undefined: результат - один из всех возможных порядков оценки. undefined поведение означает, что программа может вести себя так, как она хочет, включая получение ожидаемого результата (ожидаемого программистом) для рассматриваемого выражения при одновременном обновлении всех других данных.