Constant expressions are expressions that are possibly evaluated by the compiler at compile-time. Only non-complex computations can be carried out in a constant expression (these rules are progressively relaxed in later versions). Use the constexpr specifier to indicate the variable, function, etc. is a constant expression.

constexpr int square(int x) {
  return x * x;
}

int square2(int x) {
  return x * x;
}

int a = square(2);  // mov DWORD PTR [rbp-4], 4

int b = square2(2); // mov edi, 2
                    // call square2(int)
                    // mov DWORD PTR [rbp-8], eax

In the previous snippet, notice that the computation when calling square is carried out at compile-time, and then the result is embedded in the code generation, while square2 is called at run-time.

constexpr values are those that the compiler can evaluate, but are not guaranteed to, at compile-time:

const int x = 123;
constexpr const int& y = x; // error -- constexpr variable `y` must be initialized by a constant expression

Constant expressions with classes:

struct Complex {
  constexpr Complex(double r, double i) : re{r}, im{i} { }
  constexpr double real() { return re; }
  constexpr double imag() { return im; }

private:
  double re;
  double im;
};

constexpr Complex I(0, 1);