Почему в C++ разрешено несколько предварительных приращений, но не в C?

Почему

int main()
{
    int i = 0;
    ++++i;
}

действительный C++, но не действительный C?

Ответ 1

C и C++ говорят разные вещи о результате префикса ++. В C++:

[expr.pre.incr]

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

Таким образом, ++ можно снова применить к результату, потому что результат в основном заключается только в том, что объект увеличивается и является значением lvalue. В C однако:

6.5.3 Унарные операторы

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

Значение операнда оператора prefix ++ увеличивается. Результатом является новое значение операнда после инкремента.

Результат не является значением lvalue; это просто чистая величина приращения. Таким образом, вы не можете применять оператора, для которого требуется lvalue, включая ++.

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

Ответ 2

В C это всегда было так. Вероятно, потому, что pre-incremented ++ может быть оптимизирован для одной машинной кодовой инструкции на многих процессорах, в том числе с 1970-х годов, когда была разработана концепция ++.

В C++ хотя есть симметрия с перегрузкой оператора для рассмотрения. Чтобы соответствовать C, канонический предварительный инкремент ++ должен был бы возвращать const &, если у вас не было другого поведения для пользовательских и встроенных типов (что было бы запахом). Ограничение возврата const & является ухищрением. Таким образом, возврат ++ освобождается от правил C, за счет увеличения сложности компилятора, чтобы использовать любые оптимизации ЦП для встроенных типов.

Ответ 3

Я предполагаю, что вы понимаете, почему это прекрасно в C++, поэтому я не буду подробно останавливаться на этом.

Что бы это ни стоило, вот мой результат теста:

t.c:6:2: error: lvalue required as increment operand
  ++ ++c;
  ^

Что касается CppReference:

Нелинейные выражения объектов

В Colloquially, известном как rvalues, выражения non-lvalue являются выражениями типов объектов, которые не обозначают объекты, а являются значениями, которые не имеют идентификатора объекта или места хранения. Адрес выражения объекта, отличного от lvalue, не может быть выполнен.

Следующие выражения являются выражениями, отличными от lvalue:

  • все операторы, не указанные для возврата lvalues, включая

    • приращения и уменьшения (примечание: формы pre- - lvalues в C++)

Раздел 6.5.3.1 от n1570:

Значение операнда оператора prefix ++ увеличивается. Результатом является новое значение операнда после инкремента.

Таким образом, в C, результат приращения приращения префикса и операторов сокращения префикса не обязательны, чтобы быть lvalue, таким образом, не увеличиваться снова. Фактически, такое слово можно понимать как "обязательное для rvalue".

Ответ 4

Другие ответы объясняют, как стандарты расходятся в том, что им требуется. Этот ответ дает мотивирующий пример в области различия.

В C++ вы можете иметь такую функцию, как int& foo(int&); , который не имеет аналога в C. Для C++ полезно (а не обременительно) иметь возможность foo(foo(x)); ,

Представьте себе, что операции над базовыми типами были определены где-то, например int& operator++(int&); , ++++x сам по себе не является мотивирующим примером, но он соответствует шаблону foo выше.