Почему «++ i ++» недействителен, в то время как (++ i) ++ допустим?

14

Давайте рассмотрим следующий код:

int main() {
    int i = 2;
    int b = ++i++;
    return 3;
}

Он компилирует следующее с ошибкой:

<source>: In function 'int main()':

<source>:3:16: error: lvalue required as increment operand

    3 |     int b = ++i++;

      |                ^~

Это звучит справедливо для меня. Приращение постфикса имеет более высокий приоритет, чем приращение префикса, поэтому код анализируется как int b = ++(i++);и iявляется r-значением. Отсюда и ошибка.

Давайте теперь рассмотрим этот вариант с круглыми скобками, чтобы переопределить приоритеты по умолчанию:

int main() {
    int i = 2;
    int b = (++i)++;
    return 3;
}

Этот код компилируется и возвращает 3. Сам по себе, это звучит справедливо для меня, но кажется, что это противоречит первому коду.

Вопрос: почему (++i)это lvalueкогда iнет?

Спасибо!

ОБНОВЛЕНИЕ: сообщение об ошибке, показанное выше, было от gcc (x86-64 9.2). Вот точный рендеринг: ошибка с gcc

Clang x86-64 9.0.0 имеет совершенно другое сообщение: ошибка с clang

<source>:3:13: error: expression is not assignable

    int b = ++i++;

            ^ ~~~

С GCC у вас создается впечатление, что проблема в постфиксном операторе, и вы можете узнать, почему ++iвсе в порядке, а iчто нет, отсюда и мой вопрос. С Clang становится понятнее, что проблема с префиксным оператором.

Bktero
источник
Первоначально он был помечен
Антти Хаапала,
Действительно извините! Я предположил, что поведение было таким же в C ...
Bktero

Ответы:

23

iи ++iоба являются lvalues, но i++являются rvalue.

++(i++)не может быть действительным, так как префикс ++прикладывается к i++, который является Rvalue. Но (++i)++это хорошо, потому что ++iэто lvalue.

Обратите внимание, что в C ситуация другая; i++и ++iоба значения. (Это пример того, почему люди должны перестать предполагать, что C и C ++ имеют одинаковые правила. Люди вставляют эти предположения в свои вопросы, которые затем должны быть опровергнуты.)

Брайан
источник
4

Эта декларация

int b = ++i++;

эквивалентно

int b = ++( i++ );

Оператор постфиксного приращения возвращает значение операнда перед приращением.

Из стандарта C ++ 17 (8.2.6 Увеличение и уменьшение)

1 Значение выражения postfix ++ - это значение его операнда ... Результат - значение .

В то время как унарный оператор приращения возвращает lvalue после его приращения. Так что эта декларация

int b = (++i)++;

действует. Вы могли бы, например, написать

int b = (++++++++i)++;

Из стандарта C ++ 17 (8.3.2 Увеличение и уменьшение)

1 Операнд префикса ++ изменяется путем добавления 1. Операнд должен быть изменяемым lvalue. Тип операнда должен быть арифметическим типом, отличным от cv bool, или указателем на полностью определенный тип объекта. Результатом является обновленный операнд; это lvalue , и это битовое поле, если операнд является битовым полем ....

Обратите внимание, что в Си оба оператора возвращают значение вместо lvalue. Так что в C это объявление

int b = (++i)++;

является недействительным.

Влад из Москвы
источник
3

поэтому код анализируется как int b = ++ (i ++); и я это ценность.

Нет, iэто не ценность. iэто lvalue. i++является rvalue (prvalue, чтобы быть конкретным).

eerorika
источник