Использование --x++ допустимо?

106
24 февраля 2021, 23:50

Есть пара вопросов о допустимости и корректности записи вроде --x++

  1. Верно ли утверждать, что (--x)++ представляет собой некоторый эквивалент (x-1)? Т. е. верно ли, что этот код корректен и НЕ порождает неопределённое поведение?

  2. Почему приходится ставить скобки вокруг декремента? Я ожидал, что без скобок компилятор в конструкции --x++ сначала возьмёт префиксную операцию, а потом постфиксную, но по какой-то причине он делает наоборот:

    error: lvalue required as decrement operand
       cout << --x++ << endl;
                 ^~
    

    Является ли такое поведение компилятора верным?

Пример программы: https://ideone.com/q0Wu6U

#include <iostream>
using namespace std;
int main()
{
  int x = 8;
  cout << (--x)++ << endl;
  cout << x << endl;
  return 0;
}
Answer 1
  1. По определению, выражение (--x)++ ведет себя как (x = x - 1)++. Начиная с С++17 процесс вычисления оператора присваивания упорядочен (sequenced) очень строго: правая часть полностью упорядочена перед левой частью и сам акт присваивания упорядочен перед вычислением результата оператора присваивания.

    То есть процесс вычисления оператора присваивания a = b в С++17 разделили на изолированные друг от друга "отсеки" [[a] = [b]], обрабатываемые справа-налево

    • Сначала вычисляется значение b и выполняются все побочные эффекты, присутствующие в b

    • Затем вычисляется значение a и выполняются все побочные эффекты, присутствующие в a

    • Затем происходит собственно присваивание

    • И только после этого результат выражения a = b возвращается в контекст более высокого уровня
       

    Это означает, что случае (--x)++ неопределенного поведения нет. Все побочные эффекты и чтения значений строго упорядочены относительно друг друга. Это выражение не меняет значения x, а его результат - это исходное значение x минус 1.

    В С++14 и ранее ситуация могла быть иной... Например, до C++17 выражение i = i++ порождало неопределенное поведение, а начиная с C++17 поведение этого выражения уже полностью определено. Однако в вашем конкретном примере все в порядке и в С++11. Формально в С++98 поведение не определено, но даже это признано дефектом стандарта С++98.

    При этом стоит заметить, что несмотря на довольно строгую упорядоченность, которую С++17 внес в процесс вычисления индивидуального оператора присваивания, вышеупомянутые "отсеки" не гарантируют изолированности процессов вычисления нескольких независимых операторов присваивания друг от друга. Выражение (i = 1) + (i = 2) по-прежнему порождает неопределенное поведение, потому что побочный эффект i = 1 не упорядочен относительно побочного эффекта i = 2.

  2. В С и С++ во все века и времена постфиксные операторы имели больший приоритет, чем префиксные. Поэтому мне не ясно, почему вы ожидали, что --x++ может быть корректным выражением.

Answer 2

Увидев оператор -- компилятор ожидает обьект, а так как x++ возвращает временный обьект, то эта операция становится не действительным. Со скобкой эта проблема отпадает: сначала значение обьекта уменьшается, а потом для этого же обьекта выполняется оператор ++

По поводу первого вопроса: не вижу ничего некорректного и нет причин для неопределенного поведения... P.S. Неопределенное поведение в таких выражениях как --x + x-- из за того, что стандарт не определяет порядок вычисления операндов(что сначала вычислять --x или x-- ? ) В случаи в вопросе, этой проблемы нет.

Answer 3

Будет неопредленное поведение, у вас два изменения в одной точке следования. Скобки точку не создают.

Answer 4

1) Приоритет операций: http://cppstudio.com/post/302/ сначала постфиксный, потом префиксный. 2) Постфиксный инкремент возвращает ссылку на временный объект (rvalue), а префиксный декремент принимает ссылку только на постоянный объект (lvalue). 3) Неопределённого поведения здесь порождать нечему. --x возвращает lvalue (ссылку на х), (--x)++ возвращает rvalue - (копию (--x)) + 1. Операция вывода с rvalue объектами работает. Более того, если включить оптимизацию, он этот код заменит прямо на х-1:

int square(int num) {
    return (--num)++;
}

gcc9.1 -O2

leal    -1(%rdi), %eax
ret

https://godbolt.org/z/rq1uA-

READ ALSO
Перевести SQL-запрос в LINQ

Перевести SQL-запрос в LINQ

Есть два DataGridViewВ одном главные записи, во втором детали по нему

95
Потокобезопасный перебор коллекции

Потокобезопасный перебор коллекции

Подскажите, как можно реализовать коллекцию, реализующую INotifyCollectionChanged, и которую можно было бы потокобезопасно перебирать? Наследоваться...

93
C# атрибут XML повторяющий

C# атрибут XML повторяющий

прошу Вас помогите 3 дня уже почти сижу не могу понять как сделатьЕсть ХМЛ документ вот пример: Как видите, тут два атрибута <"ValType Type="> повторяются,...

104