An expression is a sequence of operators and operands [expr]. Each has a type and a value category, and may have side effects. An operator on class or enumeration operands may be overloaded; this changes the meaning but not the precedence.
Precedence and associativity
The standard defines these through its grammar and gives no numbered levels. The levels below are the conventional numbering. Each line binds tighter than the ones below.
| Level | Operator | Description | Associativity |
|---|---|---|---|
| 1 | :: | Scope resolution | Left to right |
| 2 | a++ a-- | Postfix increment and decrement | Left to right |
type(a) type{a} | Functional cast | ||
a() | Function call | ||
a[] | Subscript | ||
a.b a->b | Member access | ||
| 3 | ++a --a | Prefix increment and decrement | Right to left |
+a -a | Unary plus and minus | ||
!a ~a | Logical and bitwise not | ||
(type)a | C-style cast | ||
*a | Indirection | ||
&a | Address-of | ||
sizeof | Size-of | ||
co_await | Await expression (C++20) | ||
new new[] delete delete[] | Dynamic allocation and release | ||
| 4 | a.*b a->*b | Pointer to member | Left to right |
| 5 | a*b a/b a%b | Multiplication, division, remainder | Left to right |
| 6 | a+b a-b | Addition and subtraction | Left to right |
| 7 | a<<b a>>b | Bit shifts | Left to right |
| 8 | a<=>b | Three-way comparison (C++20) | Left to right |
| 9 | a<b a<=b a>b a>=b | Relational | Left to right |
| 10 | a==b a!=b | Equality | Left to right |
| 11 | a&b | Bitwise and | Left to right |
| 12 | a^b | Bitwise exclusive or | Left to right |
| 13 | a|b | Bitwise or | Left to right |
| 14 | a&&b | Logical and | Left to right |
| 15 | a||b | Logical or | Left to right |
| 16 | a?b:c | Conditional | Right to left |
throw | Throw expression | ||
co_yield | Yield expression (C++20) | ||
a=b | Assignment | ||
a+=b a-=b a*=b a/=b a%=b | Compound assignment | ||
a<<=b a>>=b a&=b a^=b a|=b | Compound assignment | ||
| 17 | a,b | Comma | Left to right |
- The expression between
?and:is parsed as if parenthesised. sizeof (int) * pparses as(sizeof(int)) * p, because the operand ofsizeofcannot be a C-style cast.*p++is*(p++), anda = b = cisa = (b = c).std::cout << a & bparses as(std::cout << a) & b.- The cast keywords,
typeid,alignof,noexceptandsizeof...are not listed because their syntax is never ambiguous. - The named alternative tokens, such as
and, behave like the operators they spell.
Evaluation order
Precedence controls grouping, not when operands run. Evaluation order is mostly unspecified, but C++17 added rules for many operators [intro.execution], [expr.call].
A full-expression finishes before the next one starts. Within one, the rules below say what is sequenced before what. If two side effects on the same scalar object, or a side effect and a value computation of it, are not sequenced, the behaviour is undefined.
| Expression | Order | Since |
|---|---|---|
a && b, a || b | a first, and b may not run | C++98 |
a ? b : c | a first, and only one of b and c runs | C++98 |
a, b | a first (built-in comma) | C++98 |
a.b, a->b, a.*b, a->*b | a first | C++17 |
a[b] | a first | C++17 |
a << b, a >> b | a first | C++17 |
a = b, a op= b | b first, then a | C++17 |
f(args) | f first; arguments in an unspecified order, with no interleaving | C++17 |
new T(args) | Allocation first, then the arguments | C++17 |
i = i++ + 1; // defined since C++17
a[i] = i++; // defined since C++17: i++ runs first
i + i++; // undefined: value computation and side effect are unsequenced
f(i++, i++); // not undefined since C++17, but the order is unspecified
g(h(), j()); // h and j run in an unspecified order
An overloaded operator used with operator notation follows the sequencing of the built-in operator [over.match.oper].
Operator details
| Operator | Rule | Section |
|---|---|---|
/ | Integer division truncates towards zero. Division by zero is undefined behaviour, as is an overflow such as the smallest int divided by -1 | [expr.mul] |
% | The result has the sign of the dividend. (a/b)*b + a%b equals a | [expr.mul] |
+, - | Signed overflow is undefined. Unsigned arithmetic is modulo 2N. Pointer arithmetic must stay within one array, including one past its end | [expr.add] |
<<, >> | A count that is negative, or not less than the width, is undefined. Since C++20, a << b is modulo 2N, and a >> b is arithmetic, dividing with rounding down | [expr.shift] |
<=> | See Three-way comparison | [expr.spaceship] |
&&, || | Short-circuit, for built-in operands | [expr.log.and], [expr.log.or] |
?: | The second and third operands are converted to a common type. With both lvalues of one type, the result is an lvalue | [expr.cond] |
sizeof | The size in bytes of a type or unevaluated expression. sizeof... counts the elements of a pack | [expr.sizeof] |
alignof | The alignment of a type (C++11) | [expr.alignof] |
noexcept(e) | true if the expression cannot throw (C++11) | [expr.unary.noexcept] |
typeid | A std::type_info. It evaluates the operand only for a glvalue of polymorphic type | [expr.typeid] |
[] | a[b] is *(a+b) for built-in types. a[i, j] is multidimensional subscripting since C++23 | [expr.sub] |
new and delete
A new-expression obtains storage for an object from an allocation function, then constructs it [expr.new]. A delete-expression reverses this [expr.delete].
int* a = new int; // default-initialised
int* b = new int(5); // direct-initialised
int* c = new int{5}; // list-initialised
int* d = new int[3]{1, 2}; // array: the last element is zero-initialised
int* e = new int[]{1, 2, 3}; // bound deduced (C++20)
int* f = new (std::nothrow) int; // null on failure instead of throwing
Widget* g = new (buffer) Widget(); // placement new
delete a;
delete[] d;
g->~Widget(); // placement new needs an explicit destructor call
| Allocation function | Used by |
|---|---|
void* operator new(std::size_t) | new T |
void* operator new[](std::size_t) | new T[n] |
void* operator new(std::size_t, std::align_val_t) | Over-aligned types (C++17) |
void* operator new(std::size_t, const std::nothrow_t&) noexcept | new (std::nothrow) T |
void* operator new(std::size_t, void*) | Placement new: constructs at the given address and does not allocate |
- Allocation failure throws
std::bad_alloc, unless the form isnothrow. deleteon a pointer fromnew[], ordelete[]on one fromnew, is undefined behaviour.- Deleting a null pointer does nothing.
- A class can define its own
operator newandoperator delete. Replacing the global ones is allowed too. - A size-taking
operator deleteexists since C++14. - Since C++14, a compiler may combine or omit calls to the replaceable allocation functions.
- If a constructor throws, the matching deallocation function is called on the storage.
Casts
| Cast | Converts | Section |
|---|---|---|
static_cast<T>(e) | Between related types: numeric conversions, base and derived, void* to object pointer, scoped enumeration to integer | [expr.static.cast] |
const_cast<T>(e) | Adds or removes const and volatile. Modifying an object defined const through the result is undefined | [expr.const.cast] |
dynamic_cast<T>(e) | Up, down and across a polymorphic hierarchy, checked at run time. A failed pointer cast gives null, and a failed reference cast throws std::bad_cast | [expr.dynamic.cast] |
reinterpret_cast<T>(e) | Between unrelated pointers and between pointers and integers, without changing the bits | [expr.reinterpret.cast] |
(T)e | The C-style cast. Tries, in order, const_cast, static_cast, static_cast then const_cast, reinterpret_cast, and reinterpret_cast then const_cast | [expr.cast] |
T(e), T{e} | Functional casts: T is constructed from the operands | [expr.type.conv] |
auto(e), auto{e} | Decay-copy (C++23) | [expr.type.conv] |
std::bit_cast<T>(e) (C++20) from <bit> reinterprets an object’s
bytes as another trivially copyable type of the same size. It is not an operator, but it
is usually a better choice than reinterpret_cast, which can break
aliasing rules.