Expressions

Expressions are something that can be evaluated to produce a value.

The simplest kind of expression is when you write a variable name or a single literal. You can also make a more sophisticated expressions using operators.

Operators

Arithmetic operators

Unary arithmetic operators
  • +a (unary plus) gets the value of a (unnecesarry in most cases), and promote the type of the result to int
  • -a (unary minus) negates the value of a (plus to minus, vice versa), and promote the type of the result to int
Binary arithmetic operators
  • a + b (addition) adds a and b
  • a - b (subtraction) subtracts a with b
  • a * b (multiplication) multiplies a and b
  • a / b (division) divides a with b
  • a % b (modulo) computes the reminder of a divided by b
Increment/decrement operators
  • a++ gets the current value of a, then increments the value of a by 1
  • a-- gets the current value of a, then decrements the value of a by 1
  • ++a increments the value of a by 1, then gets the new value of a
  • --a decrements the value of a by 1, then gets the new value of a

Assignment operators

Normal assignment
  • a = b assigns b to a, then gets the value of a
Compound assignment
  • a += b equivalent to a = a + b
  • a -= b equivalent to a = a - b
  • a *= b equivalent to a = a * b
  • a /= b equivalent to a = a / b
  • a %= b equivalent to a = a % b
  • a &= b equivalent to a = a & b
  • a |= b equivalent to a = a | b
  • a ^= b equivalent to a = a ^ b
  • a <<= b equivalent to a = a << b
  • a >>= b equivalent to a = a >> b

Relational operators

Equality operators
  • a == b (equality) returns true if a = b, otherwise return false
  • a != b (inequality) returns true if a b, otherwise return false
Comparison operators
  • a < b returns true if a < b, otherwise return false
  • a > b returns true if a > b, otherwise return false
  • a <= b returns true if a b, otherwise return false
  • a >= b returns true if a b, otherwise return false

Logical operators

Logical operators
  • a && b (logical AND) returns true if a and b is both true, false otherwise
  • a || b (logical OR) returns true if either a or b is true, false otherwise
  • !a (logical NOT) returns true if a is false, false otherwise
Bitwise operators
  • a & b (bitwise AND) performs boolean AND operation on each bit of a and b, e.g. 0b0011 & 0b0101 0b0001
  • a | b (bitwise OR) performs boolean OR operation on each bit of a and b, e.g. 0b0011 | 0b0101 0b0111
  • a ^ b (bitwise XOR) performs boolean XOR operation on each bit of a and b, e.g. 0b0011 ^ 0b0101 0b0110
  • ~a (bitwise NOT) inverts all bits of a
Bitwise shift operators
  • a << b (shift left) shifts in b zero bits to the right of a (the left-most b bits are discarded), e.g. 0b0011 << 1 0b0110
  • a >> b (shift right) shifts in b zero bits to the left of a (the right-most b bits are discarded), e.g. 0b0011 >> 1 0b0001

Miscellaneous operators

Subscript operator
  • a[b] gets the element at index b from array a
Member access operators
  • a.b gets member b from object a
  • a->b gets member b from pointer a
Pointer operators
  • &a gets the address of a
  • *a gets the value stored by pointer a
Scope resolution operator
  • a::b gets member b in class/namespace a
  • ::b gets variable b in the global namespace
Function calls
  • fn() calls function fn, optionally with parameters (e.g. fn(a, b, c)), returning the return value from the function call
Throw operator
  • throw a throws an exception with the value a (which could be an error code, a description of the problem, or a custom exception class, learncpp.com)
Allocation/deallocation operators
  • new type allocates storage for an object of type type, returning a pointer to the allocated object
  • new type[] allocates storage for an array of type type, optionally with length (e.g. new type[length]), returning a pointer to the allocated array
  • delete a deallocates storage for object a ("deallocates a single variable")
  • delete[] a deallocates storage for array a ("deallocates multiple variables")
Casting operators
  • type(a) casts a to the new type type (functional cast)
  • type{a} casts a to the new type type (functional cast, C++11)
  • (type)a casts a to the new type type (C-style cast)
  • static_cast<type>(expr) casts expr to type type
  • dynamic_cast<type>(expr) casts expr to type type with runtime type-checking (becomes nullptr if the casting is invalid)
  • const_cast<type>(expr) removes const property from pointer or reference expr
  • reinterpret_cast<type>(expr) interprets the value stored at address expr as type type (usually for low-level programming)
Other operators
  • a ? b : c (ternary operator) if a is true, returns b, otherwise returns c
  • a <=> b (three-way comparison) if a < b, returns a negative value; if a > b, returns a positive value; if a == b, returns 0
  • a, b (comma operator) RARELY USED: evaluates a, then evaluates b, then returns b (returns the right-most operand)
  • sizeof(a) gets the size of object a OR type a, in bytes

Operator precendence and associativity

When multiple operator appears in one statement, operator precendence and operator associativity rules govern how to read the expression (which part should be performed first)

Operator precendence

Operators with higher precendence should be performed first.

  • Use parentheses (...) to enforce precendence
  • Remember PEMDAS in math (*'s and /'s are performed before +'s and -'s)
  • Logical AND has higher precendence than OR (&&'s are performed before ||'s)

Operator associativity

Just use common sense. You shouldn't remember these rules.

If two operators have the same precendence (e.g. / and *), operator associativity rule tells us whether we should read it left-to-right or right-to-left.

For example, a * b * c should be read as (a * b) * c, because * have left-to-right associativity (read it from left to right).

Evaluation order

Operator precendence and associativity rules only tell which part of the expression should be evaluated first in relation to other operators.

Given a + (b * c) or f(a, b, c), the specification does not tell whether the compiler should evaluate a, b, or c first. The compiler is free to choose whatever order it likes (for optimization).

Enforcing evaluation order

To enforce certain evaluation order, separate the expression into separate statements:

// The compiler can choose any evaluation order
// e.g. `roll()` can be called first before `stop()`
result = (stop() * drop()) + roll();

// Enforcing evaluation order
int temp =
result = stop();
result += drop();
result += roll();

Lazy evaluation

Also called short-circuit evaluation:

  • In a && b if a is false, then the whole expression is false, so there is no need to evaluate b (it is not evaluated and its side effects are not executed)
  • In a || b if a is true, then the whole expression is true, so there is no need to evaluate b (it is not evaluated and its side effects are not executed)

References