Why doesn't c++ have &&= or ||= for booleans?

51,377

Solution 1

A bool may only be true or false in C++. As such, using &= and |= is relatively safe (even though I don’t particularly like the notation). True, they will perform bit operations rather than logical operations (and thus they won’t short-circuit) but these bit operations follow a well-defined mapping, which is effectively equivalent to the logical operations, as long as both operands are of type bool.1

Contrary to what other people have said here, a bool in C++ must never have a different value such as 2. When assigning that value to a bool, it will be converted to true as per the standard.

The only way to get an invalid value into a bool is by using reinterpret_cast on pointers:

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

But since this code results in undefined behaviour anyway, we may safely ignore this potential problem in conforming C++ code.


1 Admittedly this is a rather big caveat as Angew’s comment illustrates:

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

The reason is that b & 2 performs integer promotion such that the expression is then equivalent to static_cast<int>(b) & 2, which results in 0, which is then converted back into a bool. So it’s true that the existence of an operator &&= would improve type safety.

Solution 2

&& and & have different semantics: && will not evaluate the second operand if the first operand is false. i.e. something like

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

is safe, but

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

is not, although both operands are of type bool.

The same is true for &= and |=:

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

will behave differently than:

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

Solution 3

Short answer

All the operators +=, -=, *=, /=, &=, |=... are arithmetic and provide same expectation:

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

However, operators &&= and ||= would be logical, and these operators might be error-prone because many developers would expect foo() be always called in 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
  • Do we really need to make C/C++ even more complex to get a shortcut for x = x && foo()?

  • Do we really want to obfuscate more the cryptic statement x = x && foo()?
    Or do we want to write meaningful code like if (x) x = foo();?


Long answer

Example for &&=

If &&= operator was available, then this code:

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

is equivalent to:

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

This first code is error-prone because many developers would think f2() is always called whatever the f1() returned value. It is like writing bool ok = f1() && f2(); where f2() is called only when f1() returns true.

  • If the developer actually wants f2() to be called only when f1() returns true, therefore the second code above is less error-prone.
  • Else (the developer wants f2() to be always called), &= is sufficient:

Example for &=

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

Moreover, it is easier for compiler to optimize this above code than that below one:

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

Compare && and &

We may wonder whether the operators && and & give the same result when applied on bool values?

Let's check using the following C++ code:

#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);
}

Output:

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

Conclusion

Therefore YES we can replace && by & for bool values ;-)
So better use &= instead of &&=.
We can consider &&= as useless for booleans.

Same for ||=

operator |= is also less error-prone than ||=

If a developer wants f2() be called only when f1() returns false, instead of:

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

I advice the following more understandable alternative:

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)

or if you prefer all in one line style:

// 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();
Share:
51,377
Kache
Author by

Kache

Updated on July 16, 2020

Comments

  • Kache
    Kache almost 4 years

    Is there a "very bad thing" that can happen &&= and ||= were used as syntactic sugar for bool foo = foo && bar and bool foo = foo || bar?

    • jamesdlin
      jamesdlin about 14 years
      See this other question: stackoverflow.com/questions/2324549/… That one's about Java, but sharing the C lineage, the same arguments mostly apply.
    • Kache
      Kache about 14 years
      Basically, just c++ doesn't have it b/c they didn't put it in - languages like Ruby has it. boo...
    • Steve Jessop
      Steve Jessop about 14 years
      But in Ruby, isn't x ||= y roughly equivalent to C++ x = x ? x : y; for any type? In other words, "set to y if not already set". That's considerably more useful than C or C++ x ||= y, which (barring operator overloading) would do "set x to (bool)y unless already set". I'm not anxious to add another operator for that, it seems a bit feeble. Just write if (!x) x = (bool)y. But then, I don't really use bool variables enough to want extra operators that are only really useful with that one type.
    • Jonathan Leffler
      Jonathan Leffler almost 9 years
      I'm sure the primary reason C++ doesn't have &&= or ||= is simply that C doesn't have them. I'm reasonably sure the reason C doesn't have them is that the functionality wasn't deemed beneficial enough.
    • Jonathan Leffler
      Jonathan Leffler almost 9 years
      Also, being ultra-pedantic, the notation bool foo = foo || bar; would invoke undefined behaviour because foo is not initialized prior to the evaluation of foo || bar. Of course, this is intended to be something like bool foo = …initialization…; …; foo = foo || bar; and the question then stands as valid.
  • dan04
    dan04 about 14 years
    But the && and || operators will work on anything that converts to bool, not just bool.
  • Niki
    Niki about 14 years
    They don't do the same thing, even on bools. || and && shortcut, i.e. the second argument isn't operand if the first operand is true (resp. false for &&). |, &, |= and &= always evaluate both operands.
  • Konrad Rudolph
    Konrad Rudolph about 14 years
    @nikie: I didn’t say that they did the same. And do you really want to short-circuit an assignment such as a &&= b? I think that’s asking for trouble.
  • Kache
    Kache about 14 years
    all the more reason to have &&= in my opinion. =P
  • Johannes Schaub - litb
    Johannes Schaub - litb about 14 years
    In fact, if you try to do a switch on the above b, it is likely you end up in default: even if you have both true and false branches.
  • Konrad Rudolph
    Konrad Rudolph about 14 years
    @Johannes: The UB case? Yes, that’s possible. But I’ve tested this and on my PC (OS X 10.5, GCC 4.4.2) it actually doesn’t – which kind of surprised me, too.
  • Niklas R
    Niklas R almost 11 years
    What if I actually want this behaviour? That the right hand expression is not executed if the left hand expression is wrong. It is annoying to write the variables two times, like success = success && DoImportantStuff()
  • oHo
    oHo almost 11 years
    My advice is to write if(success) success = DoImportantStuff(). If the statement success &&= DoImportantStuff() was allowed, many developers would think DoImportantStuff() is always called whatever the value of success. Hope this answers what you wonder... I have also improved many parts of my answer. Please tell me if my answer is more understandable now? (about your comment purpose) Cheers, See you ;-)
  • pilkch
    pilkch almost 10 years
    "If the statement success &&= DoImportantStuff() was allowed, many developers would think DoImportantStuff() is always called whatever the value of success." You can say that about if (success && DoImportantStuff()) though. As long as they remember the logic behind the if syntax they should have no trouble with &&=.
  • thang
    thang about 9 years
    this doesn't answer the question of why &&= and ||= are not c++ operators.
  • Konrad Rudolph
    Konrad Rudolph about 9 years
    @ethang I chose to answer the question in the actual text body rather than the one in the title. Admittedly, it’s hard to know whether that was appropriate since the title and the body of the question contradicted each other. Yet the OP seems to be happy. At any rate, there’s no real answer to the question in the title, except “because.” — Nobody made a corresponding proposal to the standards committee, or any such proposal was vetoed.
  • einpoklum
    einpoklum over 8 years
    In addition to @dan04 's comment (from 6 years ago...) - bools very often convert to ints for all sorts of reasons, and the &&= would make sure everything is boolean again
  • einpoklum
    einpoklum over 8 years
    I don't see how people may assume that f1() always evaluates in ok &&= f(1) but won't assume it always evaluates in ok = ok && f(1) . Seems just as likely to me.
  • oHo
    oHo over 8 years
    Hi @einpoklum Good question and I think I should improve my answer... I mean when you see a statement ok=f(); or ok+=f(); or ok%=f(); or ok&=f(); you expect some value will be stored in variable ok. Therefore most of brains also expect ok&&=f(); will store a value in ok. Do you feel what I mean? There is also something I do not mention yet in the answer: sequence points. The statement ok=f(); has one sequence point: the final ; The statement ok=ok&&f(); has two sequence points: && and ;. Operators = cannot be a sequence point in languages C and C++.
  • einpoklum
    einpoklum over 8 years
    I actually expect v1 += e2 to be the syntactic sugar equivalent of v1 = v1 + e1 for variable v1 and expression e2. Just a shorthand notation, that's all.
  • arun s
    arun s almost 8 years
    "operator |= is also less error-prone than ||= " , Really? People make mistakes either ways. I just discovered an error in my code where I found that my assumption that line ok |= f(); will not evaluate 'f' if ok is already true is false.
  • oHo
    oHo almost 8 years
    Hi @aruns. Thanks for for feedback :-) Most developers will think the function f() will always be called in both cases: ok |= f(); and ok ||= f(); whatever the value of ok. This is only exact for ok |= f();. I am very surprised that you thought f() will not be called if ok was already true. Do not forget: you do not write code to be understandable for the compiler, you first write code to be understandable by your colleagues. Therefore I propose this more readable syntax: if (! ok) f();. Do you agree? Cheers ;-)
  • Angew is no longer proud of SO
    Angew is no longer proud of SO over 7 years
    It's not safe to use &= for a left-hand side of type bool, because it's perfectly possible for the right-hand side to be of type other than bool (such as islower or another C stdlib function which returns nonzero for true value). If we had the hypothetical &&=, it would probably force the right-hand side to convert to bool, which &= does not. In other words, bool b = true; b &= 2; results in b == false.
  • Konrad Rudolph
    Konrad Rudolph over 7 years
    @Angew That’s why I wrote “as long as both operands are indeed of type bool.” But you’re right that implicit conversion (and in particular integer promotion) make this usage less safe than a hypothetical &&=. I’ll add a footnote to my answer.
  • Antonio
    Antonio over 6 years
    Downvoted because your highlighting doesn't include "as long as both operands are indeed of type bool". One could read just the highlighted part and bring home the wrong message.
  • Konrad Rudolph
    Konrad Rudolph over 6 years
    @Antonio Tough crowd. 😝
  • Catskul
    Catskul about 5 years
    This isn't really an answer though.
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica almost 5 years
    This assumes that programmers will expect x = x || y and x = x && y to have different behaviours than x ||= y and x &&= y, which I... can see being a plausible assumption in some cases, but really don't like. xD
  • Apollys supports Monica
    Apollys supports Monica over 4 years
    Why is this the top answer? How is this even an answer of any sort?
  • Apollys supports Monica
    Apollys supports Monica over 4 years
    Best answer by far (really the only one that addresses the question).
  • Konrad Rudolph
    Konrad Rudolph over 4 years
    @Apollys What are you missing from it? Speculations about the language designers’ motives? These are generally seen as unreliable, subjective and off-topic here. We don’t know why. Case in point, olibre’s answer is pure speculation despite sounding authoritative. By contrast, this answer gives technical reasons for why having them isn’t all that useful, which might be the underlying cause.
  • Alexis Wilke
    Alexis Wilke almost 4 years
    Note that your reinterpret_cast<>() in this statement bool b = *reinterpret_cast<bool*>(&i); is not going to do what you think on Big Endian machines. You would need to check the size of bool (probably 1 byte, probably 8 on a CRAY, though) and based on that use an int type of the right size.
  • Konrad Rudolph
    Konrad Rudolph almost 4 years
    @AlexisWilke Absolutely. And the code is UB anyway. I could have made it more “portable” by abstracting away the size of a boolean (and endianness) but that would have distracted from the point I was making.