logo

C++ - const vs constexpr vs consteval vs constinit

Last Updated: 2022-03-26

TL;DR

const vs constexpr

  • const:
    • runtime constant
    • can only be used for non-static member functions, not functions in general.
  • constexpr: Since C++11
    • compile-time constant
    • can be used for both variables and functions.

All constexpr variables are const, constexpr member functions are NOT implicitly const.

These are equivalent:

  • const char* const
  • constexpr const char*
  • constexpr const char* const

constexpr vs consteval functions

  • constexpr functions: introduced in C++11; MAY be evaluated at compile-time, but may be at runtime.
  • consteval functions: introduced in C++20, a.k.a. immediate functions; MUST be evaluated at compile-time; always produce a compile-time expression and always visible only at compile-time. consteval can only be applied to the declaration of a function or function template
    • shall be applied only to the declaration of a function or function template

const

Declares an object as constant, i.e. once initialized, the value won't change.

Read-only, but not Immutable or always the same

A const object is read-only but this does not imply that it is immutable nor does it imply that the value is always the same.

void f(const std::string& s) {
  const int size = s.size();
  // ...
}

f("");  // size = 0
f("foo");  // size = 3

Other ways to change a const: mutable keyword and const_cast

Where to use const

TL;DR: const applies to the thing left of it, if there's nothing on the left, then it applies to the thing right of it

  • const int *: nothing on the left, so const if for int: a pointer to a constant integer. equivalent to int const *

  • int* const: a constant pointer to an integer, the pointer cannot point to other address, the content can be changed

  • const int* const: a constant pointer to a constant integer, both content and the address are read-only, equivalent to int const* const

  • int const* const*: a pointer to a const pointer to a constant integer

  • int const* const* const: a constant pointer to a constnat pointer to a constant integer

const object

const Foo foo;
  • needs to be initialized at the time of declaration
  • attempt to change the data member of const objects results in a compile-time error.

const member function

const member function: not allowed to modify the object.

Compare:

  • const function: can be called on any type of object.
  • Non-const function: can only be called by non-const objects.

Best practice: make as many functions const as possible so that accidental changes to objects are avoided.

Function parameters

The top-level, or outermost, const qualifications of the parameter type specification are ignored.

void F(const int x);          // 1: declares F(int)
void F(int* const x);         // 2: declares F(int*)
void F(const int* const x);   // 3: declares F(const int*)

constexpr

constexpr = constant expression.

Using constexpr = moving computation from runtime to compile time. (it is NOT metaprogramming) A good talk: https://www.youtube.com/watch?v=MdrfPSUtMVM

It can be applied to:

  • variables: compile time constants. constexpr variables are implicitly const,
  • functions: return value is computable at compile time, e.g. constexpr int Foo() { return 42; }. constexpr member functions are NOT implicitly const.

Define variables constexpr in .cc files or declare them extern const in .h files.

All constexpr functions are implicitly inline: a constexpr function is just an inline function that is allowed to execute at compile time when initializing constant values.

constinit

constinit: Since C++20, asserts that a variable has static initialization (zero initialization or constant initialization).