問題の歴史
C++は元々、マクロ(#define)と定数(const)しかサポートしていませんでした。しかし、コンパイル時に値を定義する目的には、これだけでは不十分でした。C++11では、constexprというキーワードが導入され、プログラムの実行中だけでなく、コンパイル時に値を計算できるようになりました。
問題
constexprが登場するまでは、多くのタスクをマクロ(型安全性のない粗いテキスト置換)またはconstを使用して解決しなければなりませんでしたが、constではコンパイル時に式が評価されることが常に保証されるわけではありませんでした。これにより、プログラムの最適化が困難になり、予測不可能な動作が発生しました。
解決策
constexprは、宣言された式が可能な限りコンパイル時に必ず計算されることをコンパイラに保証します。これは、コンパイル時に安全かつ効率的に計算される関数、変数、さらにはクラスのコンストラクタやメソッドを宣言するために使用されます。
コードの例:
constexpr int Square(int x) { return x * x; } constexpr int size = Square(5); // sizeはコンパイル時に計算される const int arr[size] = {}; // 配列のサイズとして使用可能
主な特徴:
任意の関数をconstexprとして使用できますか?
いいえ。関数は、C++14以前では1つのreturn文を含む非常に単純なもの(C++14以降では値計算可能なコードのみ)である必要があります。
constexpr int f(int x) { return x + 2; } // ok constexpr int g(int x) { int y = x + 2; return y; } // C++14以前: コンパイルされない! 以降は可能
すべてのconstexpr変数はコンパイル時に計算されますか?
いいえ。初期化に非定数値が使用されたり、コンパイル時に計算できない式が存在した場合、エラーが発生します。
int val; // constexpr int x = f(val); // エラー: valは初期化されていません!
constexprとconstの違いは何ですか?
constは変更不可能であることだけを保証しますが、コンパイル時の計算を保証するものではありません。constexprは、可能な限りコンパイル時に値を計算することを要求します。
const int x = time(nullptr); // ok, 但し実行時に計算される constexpr int y = 42; // ok, コンパイル時に計算される
開発者はすべての円の面積計算に#define PI 3.14を使用しています。
利点:
欠点:
開発者はconstexpr double PI = 3.141592653589793;とテンプレートのconstexpr関数を使用して計算を行っています。
利点:
欠点: