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

4

159

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?

Inadvisable answered 21/3, 2010 at 19:29 Comment(6)
See this other question: #2325049 That one's about Java, but sharing the C lineage, the same arguments mostly apply.Randirandie
Basically, just c++ doesn't have it b/c they didn't put it in - languages like Ruby has it. boo...Inadvisable
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.Cinchonize
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.Thermobarograph
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.Thermobarograph
C probably didn't have them because C didn't have bool, and these operations only really make sense for bool type.Ozonosphere
G
100

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.

Gpo answered 21/3, 2010 at 20:2 Comment(16)
But the && and || operators will work on anything that converts to bool, not just bool.Riancho
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.Fechner
@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.Gpo
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.Florettaflorette
@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.Gpo
this doesn't answer the question of why &&= and ||= are not c++ operators.Crasis
@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.Gpo
In addition to @Riancho '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 againParallelogram
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.Inexorable
@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.Gpo
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.Tillich
@Tillich Tough crowd. 😝Gpo
Why is this the top answer? How is this even an answer of any sort?Arbuckle
@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.Gpo
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.Lapsus
@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.Gpo
F
51

&& 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();
Fechner answered 21/3, 2010 at 20:13 Comment(2)
all the more reason to have &&= in my opinion. =PInadvisable
This isn't really an answer though.Fitted
C
37

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();
Conversation answered 29/3, 2012 at 15:28 Comment(11)
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()Emanative
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 ;-)Conversation
"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 &&=.Thorpe
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.Parallelogram
Hi @Parallelogram 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++.Conversation
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.Parallelogram
"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.Scranton
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 ;-)Conversation
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. xDKiblah
Best answer by far (really the only one that addresses the question).Arbuckle
The first part of the long answer is wrong. They are not equivalent. It is entirely reasonable to not want the short circuit and only do something if any one of the expressions return true: b &&= dostuff1(); b &&= dostuff2(); ... if (b) set_dirty(); for example.Million
S
0

short circuiting is important, and the semantics are straightforward. A &&= B would be syntactic sugar for A = A && B.

The concept of monadic programming was not widespread in the early days of K&R, so it seems the best explanation is K&R didn't think it was important, and like the QWERTY keyboard, we've lived with it ever since.

Selfmortification answered 18/1 at 16:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.