Почему в c++ нет & & = или ||= для булевых значений?


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

3 92

3 ответа:

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

вопреки тому, что здесь говорили другие люди, a bool в C++ никогда не должно быть другого значения, такого как 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!)

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


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

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

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

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

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

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

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() чтобы всегда называться),&= is достаточно:

пример &=

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

вывод

да мы можем заменить && by & на bool значения ;-)
Так что лучше использовать &= вместо &&=.
Мы можем рассмотреть &&= как бесполезно для булевых значений.

то же самое для ||=

оператор |= менее ошибки чем ||=

если a разработчик хочет 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();