CppOperator Precedence

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

    • (binary)

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.

CPP
// 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
<< means two different things depending on context
C++ overloads << 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.

CPP
#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;
}
Stepping through it: without parentheses, 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 operators

  • Assignment operators have the lowest precedence and are right-associative, which is why a = b = c = 0; works as expected

  • When mixing operator categories in one expression — especially involving << — add parentheses defensively

Note
Precedence determines *grouping*, not *evaluation order* between unrelated subexpressions or function-call arguments — those are governed by separate sequencing rules. Precedence only tells you how an expression is parsed, not the order side effects occur in.