134

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?

Alexis Wilke
  • 15,168
  • 8
  • 60
  • 116
Kache
  • 11,723
  • 10
  • 47
  • 71
  • 5
    See this other question: http://stackoverflow.com/questions/2324549/why-doesnt-java-have-compound-assignment-versions-of-the-conditional-and-and-con That one's about Java, but sharing the C lineage, the same arguments mostly apply. – jamesdlin Mar 21 '10 at 19:38
  • Basically, just c++ doesn't have it b/c they didn't put it in - languages like Ruby has it. boo... – Kache Mar 21 '10 at 19:54
  • 2
    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. – Steve Jessop Mar 21 '10 at 21:26
  • 1
    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 Jul 12 '15 at 18:09
  • 2
    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. – Jonathan Leffler Jul 12 '15 at 18:29

3 Answers3

79

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.

Konrad Rudolph
  • 482,603
  • 120
  • 884
  • 1,141
  • 4
    But the && and || operators will work on anything that *converts* to bool, not just bool. – dan04 Mar 21 '10 at 20:08
  • 13
    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. – Niki Mar 21 '10 at 20:09
  • 1
    @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. – Konrad Rudolph Mar 21 '10 at 20:15
  • 1
    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. – Johannes Schaub - litb Mar 21 '10 at 23:11
  • @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. – Konrad Rudolph Mar 22 '10 at 07:38
  • 4
    this doesn't answer the question of why &&= and ||= are not c++ operators. – thang Mar 16 '15 at 21:51
  • 1
    @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. – Konrad Rudolph Mar 16 '15 at 22:11
  • 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 Feb 02 '16 at 13:37
  • 11
    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`. – Angew is no longer proud of SO Nov 10 '16 at 14:02
  • @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. – Konrad Rudolph Nov 10 '16 at 14:10
  • 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. – Antonio Nov 15 '17 at 23:57
  • 2
    @Antonio Tough crowd. – Konrad Rudolph Nov 16 '17 at 09:29
  • Why is this the top answer? How is this even an answer of any sort? – Apollys supports Monica Oct 09 '19 at 23:30
  • @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. – Konrad Rudolph Oct 10 '19 at 08:54
  • Note that your `reinterpret_cast<>()` in this statement `bool b = *reinterpret_cast(&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. – Alexis Wilke Jul 15 '20 at 17:54
  • @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. – Konrad Rudolph Jul 16 '20 at 07:55
47

&& 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();
Niki
  • 15,188
  • 5
  • 41
  • 72
29

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();
oHo
  • 41,098
  • 25
  • 141
  • 183
  • 13
    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()` – Niklas R Jul 19 '13 at 08:29
  • 1
    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 ;-) – oHo Jul 19 '13 at 11:39
  • 9
    "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 `&&=`. – pilkch Jun 05 '14 at 00:13
  • 2
    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. – einpoklum Feb 02 '16 at 13:48
  • 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++. – oHo Feb 05 '16 at 21:40
  • 1
    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. – einpoklum Feb 05 '16 at 22:28
  • "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. – arun s Jun 15 '16 at 05:17
  • 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 ;-) – oHo Jun 15 '16 at 06:59
  • 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 – Justin Time - Reinstate Monica Jul 23 '19 at 21:01
  • Best answer by far (really the only one that addresses the question). – Apollys supports Monica Oct 09 '19 at 23:37