логический оператор ++ и -

104

Сегодня, когда я писал код Visual C ++, я натолкнулся на кое-что, что меня удивило. Кажется, C ++ поддерживает ++ (приращение) для bool, но не - (декремент). Это случайное решение или есть причина?

Это компилирует:

static HMODULE hMod = NULL;
static bool once = false;
if (!once++)
    hMod = LoadLibrary("xxx");

Это не так:

static HMODULE hMod = NULL;
static bool once = true;
if (once--)
    hMod = LoadLibrary("xxx");
Suma
источник
2
хм, то же самое для компилятора xcode и gcc
Владимир
Да, ++onceи once++работайте с gcc, но не с декрементами.
Джастин Ардини,
Может быть, пометьте «историю» вместо «оператор-ключевое слово», так что это сгруппировано со всеми другими забавными объяснениями того, почему различные безумные вещи разумны, если вы рассматриваете историю? :)
Джон Ханна
Обратите внимание, что начиная с C ++ 17 оператор предварительного приращения boolустарел, souce .
cogle 03
это можно заменить на std::exchange(once,false) (примечание: не атомарно), если вы хотите что-то нерекомендуемое.
golvok

Ответы:

90

Это происходит из истории использования целочисленных значений в качестве логических.

Если xесть int, но я использую его как логическое значение согласно if(x)...then, приращение будет означать, что независимо от его истинного значения перед операцией, оно будет иметь истинное значение trueпосле него (исключение переполнения).

Однако невозможно предсказать результат --данного знания только истинного значения x, так как это может привести к false(если целое значение равно 1) или true(если целое значение другое - в частности, это включает 0 [ false] и 2 или подробнее [ true]).

Так как короткометражка ++работала, так и --не работала .

++ разрешено в bools для совместимости с этим, но его использование не рекомендуется в стандарте.


Это предполагает, что я использую толькоx как логическое значение, что означает, что переполнение не может произойти, пока я не сделаю ++достаточно часто, чтобы вызвать переполнение самостоятельно. Даже с char в качестве используемого типа и CHAR_BITSчем-то низким, например, 5, это 32 раза, прежде чем это больше не сработает (это все еще достаточный аргумент в пользу того, что это плохая практика, я не защищаю эту практику, а просто объясняю, почему она работает) для 32-разрядной intверсии нам, конечно, придется использовать ++2 ^ 32 раза, прежде чем это станет проблемой. С участием-- , хотя это будет только в результате , falseесли бы я начал со значением 1 для true, или начал с 0 и используется ++точно однажды.

Это другое дело, если мы начнем со значения, которое всего на несколько единиц ниже 0. Действительно, в таком случае мы могли бы захотеть ++ бы в falseконечном итоге получить значение, например:

int x = -5;
while(++x)
  doSomething(x);

Однако этот пример рассматривается xкакint везде, кроме условного, поэтому он эквивалентен:

int x = -5;
while(++x != 0)
  doSomething(x);

Это отличается от использования только xкак логического.

Джон Ханна
источник
1
Спасибо. Приятно знать, что я все еще могу давать такие ответы людям, как это, учитывая, сколько времени прошло с тех пор, как я на самом деле написал строчку на C ++ :)
Джон Ханна
8
Но если бы x был -1 (ИСТИНА на некоторых платформах, таких как VB), ++ x было бы ЛОЖЬ.
Джеймс Карран
4
@James, в C и C ++ это был бы тот случай, о котором я думал, когда сказал («запретить переполнение»). Фактически в VB любое ненулевое значение имеет значение истинности ИСТИНА (как в C), но они имеют -1, а не 1 в результате истинных логических операций, поскольку тогда НЕ (ИСТИНА) равно ЛОЖЬ, НЕ (ЛОЖЬ) ИСТИНА, x ИЛИ TRUE равно TRUE, x OR FALSE равно x, x AND FALSE равно FALSE, x AND TRUE равно x и т. Д. С использованием одних и тех же операторов для логических и побитовых операций (поскольку VB предполагает двоичное дополнение, поэтому -1 - это все 1 бит). Однако это может вызвать некоторые странные ошибки в VB, если кодировщик не поймет, что 2 (истина) И 4 (истина) приводят к 0 (ложь).
Джон Ханна,
2
@JonHanna: ANSI C89 был первым стандартом C. Комитет ANSI C изобрел <limits.h>заголовок и CHAR_BITмакрос. До этого, я полагаю, теоретически могли быть реализации, которые charуже 8 бит, но, насколько я знаю, их не было. В частности, K & R1 (опубликовано в 1978 году) перечисляет 4 примера реализации, каждая из которых имеет 8-битную или 9-битную версию char.
Кейт Томпсон
1
@JonHanna: Соответствующая реализация C должна иметь CHAR_BIT >= 8. Стандарт не делает поправок на цели, где это сложно. (Конечно, у вас может быть несоответствующая реализация.)
Кейт Томпсон
29

ANSI ISO IEC 14882 2003 (c ++ 03):

5.2.6-2

Операнд postfix - декрементируется аналогично оператору postfix ++, за исключением того, что операнд не должен иметь тип bool. [Примечание: для увеличения и уменьшения префикса см. 5.3.2. ]

И неудивительно ...

5.3.2-2

Операнд префикса - модифицируется вычитанием 1. Операнд не должен быть типа bool. Требования к операнду префикса - и свойствам его результата в остальном такие же, как у префикса ++. [Примечание: для увеличения и уменьшения постфикса см. 5.2.6. ]

Также в 5.6.2-1 и 5.3.2-1 упоминается, что ++ для bools должно быть истинным, а в Приложении D-1 говорится, что ++ для bools устарело.

Скандинавский мэйнфрейм
источник
3
@BlueRaja: См. Ответ Джона Ханна.
Джастин Ардини,
9

По историческим причинам это было поддержано. Но обратите внимание, что ... Использование операнда типа bool с оператором ++ устарело, см. Раздел 5.3.2 в стандарте C ++ (n3092)

5.3.2 Увеличение и уменьшение [expr.pre.incr]

  • Операнд префикса ++ модифицируется добавлением 1 или устанавливается в true, если это bool (такое использование устарело). Операнд должен быть изменяемым значением. Тип операнда должен быть арифметическим типом или указателем на полностью определенный тип объекта. Результат - обновленный операнд; это lvalue, и это битовое поле, если операнд является битовым полем. Если x не относится к типу bool, выражение ++ x эквивалентно x + = 1 [Примечание: см. Обсуждение операторов сложения (5.7) и присваивания (5.17) для получения информации о преобразованиях. - конец примечания]
  • Операнд префикса - модифицируется вычитанием 1. Операнд не должен быть типа bool. Требования к операнду префикса - и свойствам его результата в остальном такие же, как у префикса ++.
Абхай
источник
3
  • Со старыми стандартами (C ++ 98) это не ошибка.
  • С новыми стандартами приращение логического значения устарело. (C ++ 11)
  • Вы можете использовать приращение для логического значения до C ++ 17.
мустафагонул
источник