оператор bool ++ і -


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");

2
хм, те саме для компілятора xcode та gcc
Володимир

Так, ++onceі once++працюйте з gcc, але не з декрементами.
Джастін Ардіні

Можливо, позначте "історію" замість "ключового слова оператора", тому це згруповано з усіма іншими веселими роз'ясненнями, чому різні божевільні речі розумні, якщо врахувати історію? :)
Джон Ханна

Зауважте, що для C ++ 17 оператор попереднього збільшення для boolзастарілого, соусе .
cogle

це можна замінити на std::exchange(once,false)(зверніть увагу: не атомне), якщо ви хочете щось непридатне.
голвок

Відповіді:


90

Він походить з історії використання цілих значень як булевих значень.

Якщо xє int, але я використовую його як булеве значення, if(x)...то нарощення означатиме, що незалежно від значення його істини перед операцією, воно буде мати значення істинності 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
Але якби х було -1 (ІСТИНА в деяких платформах, таких як VB), ++ x було б НУЖЕ.
Джеймс Курран

4
@James, в C і C ++ це був би випадок, про який я думав, коли сказав ("барринг переповнення"). Насправді в VB будь-яке ненульове значення має істину ІСТИНА (як у С), але вони мають -1, а не 1 в результаті справжніх булевих операцій, оскільки тоді НЕ (ІСТИНА) ЛІЖНЕ, НЕ (ЛІЖНЕ) - ІСТИНА, х АБО ІСТИНА - ПРАВИЛЬНА, х АБО ЛАЖНА - х, х І ЛІЖНА ЛІЖНА, а Х ІСТИНА - х і т.д., використовуючи ті самі оператори для булевих та бітових операцій (оскільки VB приймає подвійні доповнення, тож -1 все 1 біт). Однак це може викликати деякі дивні помилки в VB, якщо кодер не вловить, що 2 (true) AND 4 (true) призводить до 0 (false).
Джон Ханна

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 ++, за винятком того, що операнд не має бути 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.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.