constinit, consteval, constexpr, const の違い

constexpr関数
constexpr関数はその関数がコンパイル時にも評価可能であることを表す。静的(コンパイル時)な評価を強制される箇所でなければ動的に実行される。
メンバ関数に用いる場合にC++11では暗黙的にconst修飾されていたがC++14以降const修飾されなくなった。
普通の関数に比べて制約は多くなるが規格が進むに連れて緩和されてきた。

constexpr int func(int a) { return a*a; }
char ary[ func(16) ]; //コンパイル時定数が必要な場面で使用可能

constexpr変数
constexpr変数はその初期化がコンパイル時に行われコンパイル時定数であることを保証する。暗黙的にconstであるため変更不可能である。

constexpr int c = func(16); // 関数funcはconstexprもしくはconsteval指定されている必要がある
// c = 2; //変更不可

constinit (C++20)
constinitは変数(static or thread storage)が静的に初期化されることを保証するもの。いわゆるグローバル変数などは初期化子が定数式かどうかによって初期化順序が異なる。
constinitを使うことによって意図せず動的に初期化されることを防ぎ、初期化順序によるバグ(動的初期化と静的初期化の違いに起因するもの)を防ぐことができる。暗黙的にconst指定されない。

constinit int g = func(); //関数funcはconstexprもしくはconsteval指定されている必要がある
int main() {
 g = 2; //後から変更可。
} 

consteval (C++20)
constevalはその関数が静的に評価される事を保証する。
即値ならぬ即時関数。constexpr関数とは異なり動的に実行されることはない。

consteval int func(int a) { return a*a; }
int main() {
 int d = 2;
 func(d); //コンパイルエラー
 func(2); //OK
}

const変数
const変数はそのオブジェクトが定数であり変更不可能であることを表す。組み込みの整数型など型と初期化子によってはコンパイル時定数となれる。

const int c = 1; //この場合const int型を整数リテラル1で初期化しているためコンパイル時定数になる
// c = 2; //変更不可

int* const p = //...;
//p = nullptr; //pは変更不可

const参照/constポインタ
const参照/constポインタはその 参照/ポインタ の指す先がその 参照/ポインタ を通して変更不可能であることを表す。

int i = 1;
const int& c = i;
// c = 2; //変更不可
i = 3;
// c は 3になる

const int * p = //...;
// *p = 1; //変更不可
p = nullptr; //p自体は非const変数なので変更可

constメンバ関数
constメンバ関数はそのメンバ関数を持つクラスのオブジェクトに変更を加えない事を表明する。またそのオブジェクトがconst指定されていてもconstメンバ関数は呼び出せる。

struct S {
 int func() const { //constメンバ関数
  //m = 1; メンバ変数の変更不可
  return m;
 }
 const int func2() { //戻り値がconst変数なだけで非constメンバ関数
  m = 1; //変更可
  return m;
 }
 int m = 0;
};

const S s;
s.func(); //constのオブジェクトsに対しても呼び出せる
// s.func2(); //constのオブジェクトsに対して呼び出し不可

constexpr if文 (C++17)
if constexpr は条件をコンパイル時に評価するif文。テンプレート内で用いた場合には条件によって実行されない文の実体化を防ぐことができる。

template <class T>
void func() {
 if constexpr(false) {
    //ここの文は実体化されない
    T v; //もし依存名Tがvoid型でもコンパイルエラーとはならない
 } else {
 }
}

consteval if文(C++23)
従来のif (std::is_constant_evaluated())の上位互換。consteval関数との相性改善や誤った使い方をしにくいことなどが利点。

if consteval {
    // 静的に実行されるときのパス
}