简体   繁体   中英

Reason for the existence of non-short-circuit logical operators

When used with boolean operands, & and | become logical operators per Section 15.22.2 of the JLS . Unlike && and || , however, these don't short-circuit; they always evaluate both sides. I have a silly question: Why are the less-efficient non-short-circuit logical operators ( & , | ) still there, when we have the more-efficient short-circuit logical operators ( && , || )? I mean, what is the real usage of the non-short-circuit logical operators, as opposed to with the short-circuit logical operators? In other words, what is the usage of always evaluating both sides by using the non-short-circuit logical operators?

Updated answer :

Apologies, I missed the word "logical" in your question even though it is there. (I've taken the liberty of emphasizing it a bit with an edit.)

Consider the case where you want any side-effects to always occur, regardless of whether the left-hand expression evaluates true or false . Eg, contrast:

if (foo() & bar()) {
    // Only call this if both operations returned true
}

with

if (foo() && bar()) {
    // Only call this if both operations returned true
}

Let's assume both foo and bar have effects that we want to have happen regardless of whether foo returns true or false . In the first one above, I know that bar will always get called and have its effect. In the latter, of course, bar may or may not get called. If we didn't have the non-short-circuit version, we'd have to use temporary variables:

boolean fooResult, barResult;
fooResult = foo();
barResult = bar();
if (fooResult && barResult) {
    // ...
}

You might argue (I probably would) that you should do that anyway , because it's way too easy to misread if (foo() & bar()) , but there we go, a pragmatic reason for having non-short-circuit versions.

Original answer :

How would you propose & (or | ) be a short-circuited operator? With && and || , it makes sense because you're dealing with boolean conditions: They can be true or false, there are no shades of grey. But & and | deal with bits, not booleans. The result is a number. I mean, I guess & could not evaluate the right-hand side if the left-hand side were 0 , and similarly | could not evaluate it if the left-hand side were all-bits-on for whatever the type was, but I don't see much point to making the one edge case of each operator significant (as compared to the 254 or more other cases).

There are instances where the components of a boolean expression involve operations that you'd want to have executed in all cases. Consider the following example of checking a password for validity:

while ( !password.isValid() & (attempts++ < MAX_ATTEMPTS) ) {

    // re-prompt

}

If the second condition was not evaluated due to short-circuiting, attempts would never be incremented. Thus greater programmer flexibility is enabled.

My case (C++):

void setFields(Parameters bundle)
{
  if (setIfDifferent(&field1, bundle.value1) | 
      setIfDifferent(&field2, bundle.value2) |
      setIfDifferent(&field3, bundle.value3)) {
    storeState();
  }
}

setIfDifferent() sets the object's field with new value if they differ, in which case it returns true; or it returns false in case the field's and the new value are the same. So, we want to try to set all fields, and if any of them changed, then we want to store new object's state.

You can have some side-effects in logical expression, for example you can assign simultaneously with checking. This may work wrongly if only one part evaluated.

Can't remember good example now, but remember that I was in need of "non-short-circuit" operators sometimes.

Hmmm.... Below is WRONG example, which won't work without "non-short-circuit" OR:

if( (object1=getInstance1()).getNumber() == 1 || (object2=getInstance2()).getNumber() == 2 ) {

    // do something which requires bot object1 and object2 assigned

}

In my case, I have two methods that compare two different but related objects (Object2 is an attribute of Object1) to see if there were any changes. An update needs to happen if either are updated, but both need to be evaluated so that the objects will be modified if both have been changed. Therefore, a single pipe "OR" comparison is required.

EX:

if (compare(object1, currentObject1) | comparison(object2, currentObject2)) {
    updateObject1(object1);
}

Technically, & and | are not logical, they're bitwise operators that become logical operators when associated with booleans.

There are times when you'd want to include assignment expressions inside your logical expressions.

Say:

if(a = (checkForSomeCondition()) | b = checkForAnotherCondition())
{
 //now do something here with a and b that has been cached
}

If I had used ||, I'd not be able to perform the above check and would have had to split the assignments into separate statements. I've never come across scenarios like this during application development, but come across it a few times while writing algorithms.

Of course, you could use unary operators on the logical expressions or pass variables by reference into a predicate, but those seem like less common cases than the above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM