Operator Precedence
When an expression contains multiple operators, C++ decides which one "binds tighter" using a fixed precedence table, plus an associativity rule (left-to-right or right-to-left) for operators that share the same precedence level. Getting this wrong is a classic source of subtle bugs.
Precedence Table (Highest to Lowest)
Precedence | Operators | Associativity |
|---|---|---|
1 (highest) | :: (scope resolution) | Left-to-right |
2 | () [] . -> ++ (postfix) -- (postfix) | Left-to-right |
3 | ++ (prefix) -- (prefix) ! ~ (unary +) (unary -) sizeof | Right-to-left |
4 |
| Left-to-right |
5 |
| Left-to-right |
6 | << >> | Left-to-right |
7 | < <= > >= | Left-to-right |
8 | == != | Left-to-right |
9 | & (bitwise AND) | Left-to-right |
10 | ^ (bitwise XOR) | Left-to-right |
11 | | (bitwise OR) | Left-to-right |
12 | && | Left-to-right |
13 | || | Left-to-right |
14 | ?: (ternary) | Right-to-left |
15 (lowest) | = += -= *= /= %= &= |= ^= <<= >>= | Right-to-left |
Use Parentheses for Clarity
Even when you know the precedence rules cold, a reader skimming your code might not. Parentheses cost nothing at runtime (the compiler resolves them at compile time) and make intent explicit.
// Technically correct, but forces the reader to recall precedence rules: int result = a + b * c > d && e != f; // Much clearer with parentheses, identical behavior: int result2 = (a + (b * c)) > d && (e != f);
A Genuinely C++-Specific Gotcha: << for Shift vs Stream Insertion
<< for two unrelated purposes: as the **bitwise left-shift** operator on integers, and as the stream insertion operator for std::cout. Both meanings share the same precedence and associativity rules as the built-in shift operator — which sits lower than + and - but higher than the relational operators. This causes a very real, very common bug when printing the result of a comparison or an arithmetic expression without parentheses.#include <iostream>
int main() {
int a = 5, b = 3;
// Intent: print whether a is greater than b
// std::cout << a > b;
// This does NOT do what you expect! Operator precedence resolves it as:
// (std::cout << a) > b
// because << binds tighter than >. It prints "5", then compares the
// resulting stream object's address/bool-convertibility against b,
// which usually fails to compile or does something unintended.
std::cout << (a > b) << std::endl; // correct: parenthesize the comparison first
return 0;
}std::cout << a > b
groups as (std::cout << a) > b because <<
has higher precedence than >. The subexpression{' '}
std::cout << a prints a and evaluates to the{' '}
std::cout stream object itself, which is then (nonsensically)
compared against b. Wrapping the comparison in parentheses first
— std::cout << (a > b) — forces the comparison to happen
before the stream insertion, which is almost always the intended behavior.
Multiplicative operators (
* / %) bind tighter than additive operators (+ -)Shift operators (
<< >>) bind tighter than relational and equality operatorsAssignment operators have the lowest precedence and are right-associative, which is why
a = b = c = 0;works as expectedWhen mixing operator categories in one expression — especially involving
<<— add parentheses defensively