Подтвердить что ты не робот

Почему С++ не имеет && = или || = для booleans?

Есть ли "очень плохо" , которые могут случиться && = и || = были использованы в качестве синтаксического сахара для bool foo = foo && bar и bool foo = foo || bar?

4b9b3361

Ответ 1

A bool может быть только true или false в С++. Таким образом, с использованием &= и |= абсолютно безопасен (хотя я не особенно люблю нотацию). Правда, они будут выполнять битовые операции, а не логические операции (и, следовательно, они не будут короткими), но эти битовые операции следуют четко определенному отображению, которое фактически эквивалентно логическим операциям, , пока оба операнда тип bool. 1

Вопреки тому, что говорили другие люди, bool в С++ никогда не должен иметь другое значение, например 2. При присвоении этому значению bool он будет преобразован в true в соответствии со стандартом.

Единственный способ получить недопустимое значение в bool заключается в использовании reinterpret_cast для указателей:

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

Но так как этот код приводит к поведению undefined в любом случае, мы можем смело игнорировать эту потенциальную проблему в соответствии с кодом С++.


1 По общему признанию, это довольно большое оговорку, поскольку комментарий Ангуса иллюстрирует:

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

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

Ответ 2

&& и & имеют другую семантику: && не будет оценивать второй операнд, если первый операнд 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();

Ответ 3

Короткий ответ

Все операторы +=, -=, *=, /=, &=, |=... являются арифметическими и обеспечивают такое же ожидание:

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/С++ еще более сложным, чтобы получить ярлык для 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, поэтому второй код выше менее подвержен ошибкам.
  • Else (разработчик хочет, чтобы 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?

Проверить, используя следующий код С++:

#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
======================

Заключение

Поэтому ДА мы можем заменить && на & на 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();