Чому c ++ не має && = або || = для булевих?


126

Чи є "дуже погана річ", яка може статися, &&=і вона ||=використовувалася як синтаксичний цукор для bool foo = foo && barі bool foo = foo || bar?


5
Дивіться це інше запитання: stackoverflow.com/questions/2324549/… Це стосується Java, але для спільного використання C-лінії, в основному, застосовуються ті ж аргументи.
jamesdlin

В основному, просто у c ++ немає цього б / с, вони не ставили його - такі мови, як у Ruby, є. boo ...
Kache

2
Але в Ruby чи не x ||= yеквівалентно C ++ x = x ? x : y;для будь-якого типу? Іншими словами, "встановити y, якщо вже не встановлено". Це набагато корисніше, ніж C або C ++ x ||= y, що (перевантаження оператора заборони) зробить "встановити x, (bool)yякщо вже не встановлено". Мені не хочеться додати іншого оператора для цього, це здається трохи слабким. Просто напишіть if (!x) x = (bool)y. Але тоді я не використовую boolзмінних настільки, щоб бажати додаткових операторів, які корисні лише для одного типу.
Стів Джессоп

1
Я впевнений, що основна причина у C ++ не є, &&=або ||=це просто те, що у C їх немає. Я абсолютно впевнений, що причина C їх не полягає в тому, що функціональність не вважається достатньо корисною.
Джонатан Леффлер

2
Крім того, будучи ультра-педантичним, позначення bool foo = foo || bar;посилатиметься на невизначене поведінку, оскільки fooвоно не ініціалізується до оцінки foo || bar. Звичайно, це має бути щось на зразок, bool foo = …initialization…; …; foo = foo || bar;і питання стоїть як справедливий.
Джонатан Леффлер

Відповіді:


73

boolМоже бути тільки trueабо falseв C ++. Таким чином , використання &=і |=є відносно безпечного (хоча я не особливо подобається позначення). Щоправда, вони будуть виконувати бітові операції, а не логічні операції (і, таким чином, вони не будуть мати коротке замикання), але ці бітові операції слідують чітко визначеному відображенню, що ефективно еквівалентно логічним операціям, якщо обидва операнди мають типbool . 1

Всупереч тому, що тут казали інші люди, а boolв C ++ ніколи не повинно мати іншого значення, наприклад 2. При призначенні цього значення a bool, воно буде перетворене trueвідповідно до стандарту.

Єдиний спосіб отримати недійсне значення в a bool- це за допомогою reinterpret_castпокажчиків:

int i = 2;
bool b = *reinterpret_cast<bool*>(&i);
b |= true; // MAY yield 3 (but doesn’t on my PC!)

Але оскільки цей код все одно призводить до невизначеної поведінки, ми можемо сміливо ігнорувати цю потенційну проблему у відповідності коду C ++.


1 Справді, це досить великий застереження, як свідчить коментар Енджу:

bool b = true;
b &= 2; // yields `false`.

Причина полягає в тому, що b & 2виконується ціле просування таким чином, що вираз потім еквівалентний static_cast<int>(b) & 2, що призводить до того 0, що потім перетворюється назад в a bool. Так що це правда, що існування operator &&=A поліпшило б безпеку типу.


4
Але && і || Оператори працюватимуть над усім, що перетворюється на bool, а не просто bool.
dan04

13
Вони не роблять те ж саме, навіть на булінах. ||і &&ярлик, тобто другий аргумент не операнд, якщо перший операнд є true(відповідно falseдо &&). |, &, |=І &=завжди оцінюють обидва операнда.
Нікі

4
це не відповідає на питання, чому && = і || = не є операторами c ++.
thang

9
Це не безпечно використовувати &=для лівої частини типу bool, тому що праворуч може бути іншого типу, ніж bool(наприклад, islowerабо інша функція stdlib C, яка повертає ненульове значення для справжнього значення). Якби у нас було гіпотетичне &&=, це, ймовірно, змусило би перетворитись на праву частину bool, що &=не робить. Іншими словами, bool b = true; b &= 2;результати в b == false.
Ендже вже не пишається ТАК

2
@Antonio Жорсткий натовп. 😝
Конрад Рудольф

44

&&і &мають різну семантику: &&не буде оцінювати другий операнд, якщо перший операнд є false. тобто щось подібне

flag = (ptr != NULL) && (ptr->member > 3);

безпечно, але

flag = (ptr != NULL) & (ptr->member > 3);

ні, хоча обидва операнди мають тип bool.

Те саме стосується &=і |=:

flag = CheckFileExists();
flag = flag && CheckFileReadable();
flag = flag && CheckFileContents();

буде поводитися інакше, ніж:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();

35
тим більше причин мати && = на мою думку = Р
Каче

4
Це насправді не відповідь.
Кацкуль

27

Коротка відповідь

Всі оператори +=, -=, *=, /=, &=, |=... є арифметичними і забезпечують таке ж очікування:

x &= foo()  // We expect foo() be called whatever the value of x

Однак оператори &&=і ||=будуть логічними, і ці оператори можуть бути схильними до помилок, тому що багато розробників сподіваються, foo()що завжди зателефонують x &&= foo().

bool x;
// ...
x &&= foo();           // Many developers might be confused
x = x && foo();        // Still confusing but correct
x = x ? foo() : x;     // Understandable
x = x ? foo() : false; // Understandable
if (x) x = foo();      // Obvious
  • Чи дійсно нам потрібно зробити C / C ++ ще складнішим, щоб отримати ярлик x = x && foo()?

  • Невже ми хочемо більше придушити криптовалюту x = x && foo()?
    Або ми хочемо написати такий змістовний код if (x) x = foo();?


Довга відповідь

Приклад для &&=

Якщо &&=оператор був доступний, то цей код:

bool ok = true; //becomes false when at least a function returns false
ok &&= f1();
ok &&= f2(); //we may expect f2() is called whatever the f1() returned value

еквівалентно:

bool ok = true;
if (ok) ok = f1();
if (ok) ok = f2(); //f2() is called only when f1() returns true

Цей перший код схильний до помилок, оскільки багато розробників вважають, що f2()його завжди називають незалежно від f1()поверненого значення. Це як писати те, bool ok = f1() && f2();куди f2()дзвонять, лише коли f1()повертаються true.

  • Якщо розробник насправді хоче f2()викликати лише тоді, коли f1()повертається true, тому другий код вище менш схильний до помилок.
  • Іншого (розробник хоче, f2()щоб його завжди викликали), &=достатньо:

Приклад для &=

bool ok = true;
ok &= f1();
ok &= f2(); //f2() always called whatever the f1() returned value

Крім того, компілятору простіше оптимізувати цей вище код, ніж цей:

bool ok = true;
if (!f1())  ok = false;
if (!f2())  ok = false;  //f2() always called

Порівняйте &&і&

Ми можемо задатися питанням, чи дають оператори &&і &той же результат при застосуванні до boolзначень?

Давайте перевіримо, використовуючи наступний код C ++:

#include <iostream>

void test (int testnumber, bool a, bool b)
{
   std::cout << testnumber <<") a="<< a <<" and b="<< b <<"\n"
                "a && b = "<< (a && b)  <<"\n"
                "a &  b = "<< (a &  b)  <<"\n"
                "======================"  "\n";
}

int main ()
{
    test (1, true,  true);
    test (2, true,  false);
    test (3, false, false);
    test (4, false, true);
}

Вихід:

1) a=1 and b=1
a && b = 1
a &  b = 1
======================
2) a=1 and b=0
a && b = 0
a &  b = 0
======================
3) a=0 and b=0
a && b = 0
a &  b = 0
======================
4) a=0 and b=1
a && b = 0
a &  b = 0
======================

Висновок

Тому YES ми можемо замінити &&на &для boolзначень ;-)
Так що краще використовувати &=замість &&=.
Ми можемо вважати &&=марними булени.

Те саме для ||=

Оператор |=також менш схильний до помилок, ніж||=

Якщо розробник хоче f2()викликати лише тоді, коли f1()повертається false, а не:

bool ok = false;
ok ||= f1();
ok ||= f2(); //f2() is called only when f1() returns false
ok ||= f3(); //f3() is called only when f1() or f2() return false
ok ||= f4(); //f4() is called only when ...

Я раджу наступну більш зрозумілу альтернативу:

bool ok = false;
if (!ok) ok = f1();
if (!ok) ok = f2();
if (!ok) ok = f3();
if (!ok) ok = f4();
// no comment required here (code is enough understandable)

або якщо ви віддаєте перевагу всім в одному стилі рядка :

// this comment is required to explain to developers that 
// f2() is called only when f1() returns false, and so on...
bool ok = f1() || f2() || f3() || f4();

13
Що робити, якщо я насправді хочу такої поведінки? Що вираз правої руки не виконується, якщо вираз лівої руки неправильний. Прикро писати змінні два рази, як-отsuccess = success && DoImportantStuff()
Niklas R

1
Моя порада - написати if(success) success = DoImportantStuff(). Якби заява success &&= DoImportantStuff()була дозволена, багато розробників вважають, що DoImportantStuff()її завжди називають незалежно від значення success. Сподіваюся, це відповідає тому, що вам цікаво ... Я також вдосконалив багато частин своєї відповіді. Скажіть, будь ласка, чи моя відповідь зараз зрозуміліша? (про вашу мету коментаря)
Привіт, побачимося

9
"Якщо заяву success &&= DoImportantStuff()було дозволено, багато розробників вважають, що DoImportantStuff()це завжди називається незалежно від значення успіху." Ви можете сказати про це if (success && DoImportantStuff())хоча. Поки вони пам'ятають логіку синтаксису if, у них не повинно виникнути проблем &&=.
пікч

2
Я не бачу, як люди можуть вважати, що f1()завжди оцінює, ok &&= f(1) але не вважаю, що це завжди оцінюється в ok = ok && f(1). Мені здається так само ймовірно.
einpoklum

1
Я насправді очікую, що v1 += e2це буде синтаксичний еквівалент цукру v1 = v1 + e1для змінної v1 та виразу e2. Просто скорочення, ось і все.
einpoklum
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.