Logical and bitwise operators

Decisions in if-statements often have to be based on one or more conditions evaluating to (logically) true or false. There are three logical operators in C++ which operate on expressions and yield a boolean result; they are “and” (&&), “or” (||) and “not” (!). Modern C++ allows the keywords and, or and not to be used instead of the more traditional symbolic versions; there is no difference between the two and the keywords may even be implemented as macros.

Both of the binary operators (“or” and “and”) “short-circuit” which means that the value of the first operand determines whether the second is evaluated at all:

bool f() {
    cout << "f()\n";
    return false;
}

bool t() {
    cout << "t()\n";
    return !false;
}

auto b1 = f() && t();  // result false, t() not even called
auto b2 = t() || f();  // result true, f() not even called

Interestingly, the use of the bitwise operators (each with a single symbol instead of a double) instead is equally valid here, and bypasses the short-circuit behavior:

auto b3 = f() & t();  // result as b1, both f() and t() are called
auto b3 = t() | f();  // result as b2, both f() and t() are called

For this to work equivalently, both operands must be explicitly bool, not just implicitly convertible (values such as 0.0 and nullptr convert to false implicitly). Another way ensuring that both functions are always called would be to assign them to different variables, and apply the logical operators to these variables.

Before we write off short-circuit evaluation completely, an example of where it is not just desirable but necessary is testing a pointer against false, and only dereferencing it if non-nullptr:

void print_chars(const char *p) {
    if (p && *p) {
        cout << p << '\n';
    }
}

print_chars(nullptr);   // no output
print_chars("");        // again, no output - can you work out why?
print_chars("Booleans are awesome!");
                        // outputs this message followed by a newline

The bitwise operators work on any integer type, and also std::bitset. Where both operands have a number of set bits, as opposed to just the least significant bit, bits at equal positions are compared. They have the names bitand (&), bitor (|), bitxor (^) and compl (~). Here are some examples using two four-bit values a = 0b0110 and b = 0b1010:

OperationModern C++ styleResult
~acompl a0b1001
a & ba bitand b0b0100
a | ba bitor b0b1110
a ^ ba bitxor b0b1100

Just a quick note on bitxor, it follows that the expression a ^ a is always zero regardless of the value of a. This means that “exclusive-or” operations are always reversible, which is why they are often used in the generation of hash-values and for encryption methods.

In summary, with logical condition tests conversion of both operands to boolean are implied and the second operand is not always evaluated. With bitwise tests the operands can be boolean or integer, and in the case of the latter all bits are significant.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s