Здравствуйте, igna, Вы писали:
I>Это не undefined behavior, это unspecified behavior. И никаких "format D:", у компилятора есть выбор из двух возможностей, сначала выполнить присваивание, затем постинкрементирование или наоборот.
Или как-то несинхронизированно обратиться к памяти и обрушить какую-нибудь экзотическую платформу...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Нет, конечно. Однако, в большинстве случаев такой код лишен всякого здравого смысла. Если же ожидаются side effects от a++, то проблемы уже не в коде, а в архитектуре....
Здравствуйте, Vlad_SP, Вы писали:
F>>а если ?. F>>
F>>return a++;
F>>
F>>тоже UD?. V_S>Нет, конечно. Однако, в большинстве случаев такой код лишен всякого здравого смысла. Если же ожидаются side effects от a++, то проблемы уже не в коде, а в архитектуре....
конечно, ожидаются side effects.. например, этот a — это какой-нибудь итератор, который зачем-то хранится..
вопрос был задан только в целях самообразования..
Здравствуйте, igna, Вы писали:
I>Можно подробнее?
А что подробнее. Оба побочных эффекта могут работать РЕАЛЬНО параллельно, и это может быть авостом, выполняемым на железном уровне...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Vlad_SP, Вы писали:
V_S>Нет, конечно. Однако, в большинстве случаев такой код лишен всякого здравого смысла. Если же ожидаются side effects от a++, то проблемы уже не в коде, а в архитектуре....
Почему? Например, a может быть полем класса, из метода которого return...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, neFormal, Вы писали:
F>Здравствуйте, Андрей Тарасевич, Вы писали:
F>>>а если ?. F>>>
F>>>return a++;
F>>>
F>>>тоже UB?. АТ>>Нет. Откуда? АТ>>Осмысленность такого кода зависит от того, где определено 'a', но это уже другой вопрос.
F>например, это a — это мембер класса, а return делается в методе F>
F>a_type Get(){return a++;}
F>
F>т.е. после return-a что то ещё может быть сделано?.
А почему нет? На этом return-е программа же, надеюсь, не заканичает свое существование? Если нет, то "после return-a" еще может быть (и будет) много чего сделано. В частности, на выходе из функции стоит точка следования, значит значение 'a' увеличится еще до выхода из функции.
Здравствуйте, vadimcher, Вы писали:
V>Здравствуйте, ajanov, Вы писали:
A>>a = a++; A>>Так вот вопрос: какой должен быть результат?
V>Это еще не самое страшное! Вот код:
V>#include <iostream>
V>using namespace std;
V>int foo (int a) { return a; }
V>void main(void) {
V> int i = 1;
V> i = foo ( i++ );
V> cout << i << '\n';
V>}
V>Ответ должен быть 1? Выводит "1", если компилить в режиме "Debug", и "2" -- в режиме "Release".
А вот мне самому интересно стало. Ведь, по идее, по стандарту, данная программа ОБЯЗАНА выдать 1, т.к. я специально вставляю вызов функции, чтобы "подвести итоги" перед присваиванием. Оптимизатор же в режиме Release делает функцию насильственно inline, подставляет, вызов функции как таковой пропадает, и получается undefined, которая в случае моего компилятора выдает 2, хотя мог бы выдать и 5. Как такое поведение оптимизатора регламентируется формально, есть ли тому правила/ограничения?
Здравствуйте, vadimcher, Вы писали:
V>А вот мне самому интересно стало. Ведь, по идее, по стандарту, данная программа ОБЯЗАНА выдать 1, т.к. я специально вставляю вызов функции, чтобы "подвести итоги" перед присваиванием. Оптимизатор же в режиме Release делает функцию насильственно inline, подставляет, вызов функции как таковой пропадает, и получается undefined, которая в случае моего компилятора выдает 2, хотя мог бы выдать и 5. Как такое поведение оптимизатора регламентируется формально, есть ли тому правила/ограничения?
Возможно у тебя сотит опция, которая говорит, что не бывает двух ссылок на один объект (external aliases называется, что-ли, короче опция оптимизации a)
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, vadimcher, Вы писали:
V>>А вот мне самому интересно стало. Ведь, по идее, по стандарту, данная программа ОБЯЗАНА выдать 1, т.к. я специально вставляю вызов функции, чтобы "подвести итоги" перед присваиванием. Оптимизатор же в режиме Release делает функцию насильственно inline, подставляет, вызов функции как таковой пропадает, и получается undefined, которая в случае моего компилятора выдает 2, хотя мог бы выдать и 5. Как такое поведение оптимизатора регламентируется формально, есть ли тому правила/ограничения?
E>Возможно у тебя сотит опция, которая говорит, что не бывает двух ссылок на один объект (external aliases называется, что-ли, короче опция оптимизации a)
А что это за опция, и как она влияет в данном случае?
Здравствуйте, vadimcher, Вы писали:
V>А что это за опция, и как она влияет в данном случае? V>P.S. Сейчас посмотрю, что за зверь.
Ну оптимизатор считает, что каждая ссылка ссылается на уникальный объект. Ну типа функция
int f( int& a, int& b ) { return ++a, ++b; }
может компилироваться как
int f( int& a, int& b )
{
int res = ++b;
++a;
return res;
}
не смотря на то, что при вызове
int i = 1;
f( i, i );
будет упс...
Да, у gcc, на каком-то из уровней оптимизации по умолчанию это включено...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, ajanov, Вы писали:
A>Все доброго сремени суток!
A>Сегодня у новичка увидел в коде:
A>
A>a = a++;
A>
A>Так вот вопрос: какой должен быть результат? По правилам C++ значение a не должно измениться, однако все популярные компиляторы (MSVC, GCC) увеличивают значение на 1, а IAR выдает предуждение "undefined behavior".
operator= (a, a++);
А где в стандарте С++ описан порядок вычисления аргументов???
Здравствуйте, BigBoss, Вы писали:
BB>operator= (a, a++); BB>А где в стандарте С++ описан порядок вычисления аргументов???
Это тут не при чём, так как (если закрыть глаза на то, что так нельзя) первый аргумент -- это ссылка на a, а второй -- число. Так что тут от порядка ничего зависеть не будет. Ну а вызов функции -- это таки точка следования...
Но a тут -- стандартный тип, так что никакого operator = тут нет, и точки следования нет и всё такое...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
V>>#include <iostream>
V>>using namespace std;
V>>int foo (int a) { return a; }
V>>void main(void) {
V>> int i = 1;
V>> i = foo ( i++ );
V>> cout << i << '\n';
V>>}
V>
V>>Ответ должен быть 1? Выводит "1", если компилить в режиме "Debug", и "2" -- в режиме "Release".
V>А вот мне самому интересно стало. Ведь, по идее, по стандарту, данная программа ОБЯЗАНА выдать 1, т.к. я специально вставляю вызов функции, чтобы "подвести итоги" перед присваиванием. Оптимизатор же в режиме Release делает функцию насильственно inline, подставляет, вызов функции как таковой пропадает, и получается undefined, которая в случае моего компилятора выдает 2, хотя мог бы выдать и 5. Как такое поведение оптимизатора регламентируется формально, есть ли тому правила/ограничения?
Хорошо, поясню свой вопрос. Есть код:
i=1; i = i++;
Некто ожидает, что порядок выполнения следующий:
1) i=1;
2) i++; возвращает 1 и увеличивает i на 1, т.е. i=2;
3) i=1;
Стандарт на это говорит, что порядок вычислений следующий:
1) i=1: возвращает 1, побочное действие записать 1 в i;
2) записывает 1 в i;
3) i++ возвращает 1, побочное действие: увеличить i на 1;
4) i=i++ возвращает 1, побочное действие: записать 1 в i;
5) выполнение всех побочных действий в любом порядке.
Хорошо, стандарт есть стандарт. В соответствии со стандартом, чтобы первое побочное выполнилось до второго (строго в этом порядке) нам надо явно указать компилятору, чтобы тот перед присвоением не забыл увеличить i, а не наоборот. Для этого перед присвоением вызываем функцию, которая возвращает результат, равный значению на входе:
i = 1; i = foo ( i++ );
В соответствии со стандартом, теперь:
1) i=1: возвращает 1, побочное действие записать 1 в i;
2) записывает 1 в i;
3) i++ возвращает 1, побочное действие: увеличить i на 1;
4) 1 передается как параметр функции, перед этим выполняются все побочные эффекты: i увеличивается на 1, т.е. i=2;
5) foo() возвращает 1;
6) i=foo() возвращает 1, побочное действие: записать 1 в i;
7) выполнение всех побочных действий: i=1.
Теперь, оптимизатор. Оптимизатор, по своей сути, должен оптимизировать, чтобы код выполнялся быстрее или требовал меньше памяти, но логика программы сохранялась. Т.е. в данном случае от оптимизатора ожидается следующее:
1) i=1: возвращает 1, побочное действие записать 1 в i;
2) записывает 1 в i;
3) i++ возвращает 1, побочное действие: увеличить i на 1;
4) выполняются все побочные эффекты: i увеличивается на 1, т.е. i=2;
5) i=1 возвращает 1, побочное действие: записать 1 в i;
6) выполнение всех побочных действий: i=1.
Т.е. функция явно не вызывается, а результат ее работы подставляется. Что же делает оптимизатор? Он не просто оптимизирует выполнение кода, а он приводит выражение обратно к i=i++, а далее компилирует так, будто это и есть то, что написано в коде, при этом нарушая логику работы, когда ему явно был указан порядок выполнения увеличения i и присвоения значения в i (в соответствии со стандартом). Короче, оптимизатор оптимизирует код так, будто там есть undefined, когда его там и в помине нет.
Здравствуйте, vadimcher, Вы писали:
VV>Стандарт на это говорит, что порядок вычислений следующий:
Стандарт на это говорит, что порядок не определён, и в следствие этого получаем либо неспецифицированное, либо неопределённое (в данном случае) поведение.
Дальнейшие твои рассуждения исходят из неправильной посылки, так что их можно пропустить.
Рассмотрим выражение a = b++.
До вычисления (a,b) принимают значения (a0,b0).
После вычисления (a,b) = (b0,b0+1).
Если &a == &b, то вопрос: чему же будет равна эта переменная? Да чему попало!
Это уже, как минимум, неспецифицированное поведение.
Теперь почему у компилятора развязаны руки: фокус в том, что получить пару чисел (b0,b0+1) можно разными способами.
a = b, b = b+1
b = b+1, a = b-1
c = b, b = b+1, a = c
и т.п.
И это в том случае, если у нас строго скалярная архитектура.
А если векторная или суперскалярная, то компилятор может сформировать операцию групповой записи
(&a, &b) <- (b, b+1)
Раз нет точек следования, то и барьеров памяти (дополнительных тормозов) тоже нет. Но при групповой записи в одну ячейку может случиться страшное.
Здравствуйте, vadimcher, Вы писали:
V>>А вот мне самому интересно стало. Ведь, по идее, по стандарту, данная программа ОБЯЗАНА выдать 1, т.к. я специально вставляю вызов функции, чтобы "подвести итоги" перед присваиванием. Оптимизатор же в режиме Release делает функцию насильственно inline, подставляет, вызов функции как таковой пропадает, и получается undefined, которая в случае моего компилятора выдает 2, хотя мог бы выдать и 5. Как такое поведение оптимизатора регламентируется формально, есть ли тому правила/ограничения?
Когда-то давно это здесь уже обсуждали. Консенсус, как мне помнится, так и не был найден. На мой взгляд, поведение MSVC вполне допускается действующим стандартом.
V>Хорошо, поясню свой вопрос. Есть код:
i=1; i = i++;
V>Стандарт на это говорит, что порядок вычислений следующий:
Собственно, стандарт говорит, что "the behavior is undefined". Дальнейшие рассуждения о порядке вычислений не имеют смысла.
i = 1; i = foo ( i++ );
Тут, по моему мнению, ситуация аналогичная. С формальной точки зрения, это все та же модификация скалярного объекта между двумя соседними точками следования. Относительно побочного эффекта постинкремента мы знаем, что он должен выполниться до точки следования перед входом в функцию. Но для присваивания ограничение только одно: до конца полного выражения. И хотя здравый смысл подсказывает, что это может произойти только после выхода из функции, буква стандарта никак это не подтверждает.
Чтобы это рассуждение стало нагляднее, представьте себе гипотетическую реализацию, в которой присваивание не атомарно. Например, перед тем, как записать значение в ячейку памяти, ее необходимо очистить влажной тряпочкой.
P.S. С одной стороны, все это теперь не имеет значения, ибо точек следования больше не будет. С другой стороны, как я понимаю, новый стандарт не выйдет и в этом году...
Здравствуйте, elcste, Вы писали:
E>P.S. С одной стороны, все это теперь не имеет значения, ибо точек следования больше не будет. С другой стороны, как я понимаю, новый стандарт не выйдет и в этом году...
Как это "точек следования не будет"?
Автоматически рассосутся все проблемы с external aliasing? Или оптимизатор задушат, как автомобили с Евро-4?