constexpr 指定子 (C++11 以降)
目次 |
[編集] 解説
constexpr 指定子は、エンティティの値をコンパイル時に評価できることを宣言します。そのようなエンティティは、コンパイル時定数式のみが許可される場所で使用できます(適切な関数引数が与えられれば)。
オブジェクト宣言または非静的メンバ関数(C++14 まで)で使用されるconstexpr指定子は、constを意味します。
関数または静的データメンバ(C++17 以降)の最初の宣言で使用されるconstexpr指定子は、inlineを意味します。関数または関数テンプレートのいずれかの宣言にconstexpr指定子がある場合、すべての宣言にその指定子が含まれている必要があります。
[編集] constexpr 変数
変数または変数テンプレート(C++14 以降)は、以下のすべての条件が満たされている場合にconstexprと宣言できます。
| (C++26まで) | |
|
(C++26以降) |
|
constexpr変数が翻訳単位ローカルでない場合、定数式で使用可能な翻訳単位ローカルエンティティを参照するように初期化してはならず、またそのようなエンティティを参照するサブオブジェクトを持ってはなりません。そのような初期化は、モジュールインターフェース単位 (そのプライベートモジュールフラグメントが存在する場合、その外側) またはモジュールパーティションでは許可されず、他のコンテキストでは非推奨です。 |
(C++20以降) |
[編集] constexpr 関数
関数または関数テンプレートはconstexprと宣言できます。
関数がconstexpr-suitableであるのは、以下のすべての条件が満たされている場合です。
- コンストラクタまたはデストラクタ(C++20 以降)である場合、そのクラスは仮想基底クラスを持たないこと。
|
(C++20まで) |
|
(C++23まで) |
|
(C++20以降) |
|
(C++14まで) | ||
|
(C++14以降) (C++23まで) |
インスタンス化されたconstexpr関数を除き、非テンプレートconstexpr関数はconstexpr-suitableである必要があります。
|
デフォルト化もテンプレート化もされていない非コンストラクタconstexpr関数について、関数の呼び出しがコア定数式の評価された副式となるような引数値が存在しない場合、プログラムは ill-formed であり、診断は不要です。 テンプレート化されたconstexpr関数について、関数/クラステンプレートの特殊化が、非テンプレート関数として考慮した場合にconstexpr-suitableとならない場合、プログラムはill-formedであり、診断は不要です。 |
(C++23まで) |
特定のコンテキストでのconstexpr関数の呼び出しは、同じコンテキストでの同等の非constexpr関数の呼び出しと、以下の例外を除いて、すべての点で同じ結果を生成します。
[編集] constexpr コンストラクタ
constexpr関数の要件に加えて、コンストラクタはconstexpr-suitableであるために以下のすべての条件を満たす必要があります。
|
(C++23まで) |
- そのクラスは仮想基底クラスを持っていないこと。
|
デフォルト化もテンプレート化もされていないconstexprコンストラクタについて、関数呼び出しが定数式の対象となるオブジェクトの初期化完全式の評価された副式となるような引数値が存在しない場合、プログラムは ill-formed であり、診断は不要です。 |
(C++23まで) |
[編集] constexpr デストラクタ
|
デストラクタはconstexprにはなりませんが、自明なデストラクタは定数式で暗黙的に呼び出すことができます。 |
(C++20まで) | ||
|
constexpr関数の要件に加えて、デストラクタはconstexpr-suitableであるために以下のすべての条件を満たす必要があります。
|
(C++20以降) |
[編集] 備考
|
constexpr int f(); constexpr bool b1 = noexcept(f()); // false, undefined constexpr function constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true, f() is a constant expression |
(C++17まで) |
|
呼び出しが決してコア定数式の要件を満たさないconstexpr関数を記述することは可能です。 void f(int& i) // not a constexpr function { i = 0; } constexpr void g(int& i) // well-formed since C++23 { f(i); // unconditionally calls f, cannot be a constant expression } |
(C++23から) |
constexprコンストラクタは、リテラル型でないクラスでも許可されています。例えば、std::shared_ptrのデフォルトコンストラクタはconstexprであり、定数初期化を可能にします。
参照変数はconstexprと宣言できます (その初期化子には参照定数式が必要です)。
static constexpr int const& x = 42; // constexpr reference to a const int object // (the object has static storage duration // due to life extension by a static reference)
|
tryブロックとインラインアセンブリがconstexpr関数で許可されていても、キャッチされない(C++26 以降)例外をスローしたり、アセンブリを実行したりすることは、定数式では依然として禁止されています。 変数が定数破棄を持つ場合、そのデストラクタが自明でなくても、デストラクタを呼び出すためのマシンコードを生成する必要はありません。 非ラムダ、非特殊メンバ、非テンプレートconstexpr関数は、暗黙的に即時関数になることはできません。そのような意図された関数定義を整形式にするには、ユーザーが明示的に`consteval`とマークする必要があります。 |
(C++20以降) |
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_constexpr |
200704L |
(C++11) | constexpr |
201304L |
(C++14) | 緩和されたconstexpr、非const constexprメソッド | |
201603L |
(C++17) | Constexpr ラムダ | |
201907L |
(C++20) | constexpr 関数における自明なデフォルト初期化とasm宣言 | |
202002L |
(C++20) | 定数評価における共用体のアクティブなメンバーの変更 | |
202110L |
(C++23) | constexpr 関数における非リテラル変数、ラベル、および goto 文 | |
202207L |
(C++23) | いくつかのconstexpr制限の緩和 | |
202211L |
(C++23) | constexpr関数におけるstatic constexpr変数の許可 | |
202306L |
(C++26) | void*からのconstexprキャスト: constexpr型消去に向けて | |
__cpp_constexpr_in_decltype |
201711L |
(C++11) (DR) |
定数評価に必要とされる関数および変数の定義の生成 |
__cpp_constexpr_dynamic_alloc |
201907L |
(C++20) | constexpr 関数における動的ストレージ期間の操作 |
[編集] キーワード
[編集] 例
階乗を計算するC++11/14のconstexpr関数を定義し、文字列リテラルを拡張するリテラル型を定義します。
#include <iostream> #include <stdexcept> // C++11 constexpr functions use recursion rather than iteration constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // C++14 constexpr functions may use local variables and loops #if __cplusplus >= 201402L constexpr int factorial_cxx14(int n) { int res = 1; while (n > 1) res *= n--; return res; } #endif // C++14 // A literal class class conststr { const char* p; std::size_t sz; public: template<std::size_t N> constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {} // constexpr functions signal errors by throwing exceptions // in C++11, they must do so from the conditional operator ?: constexpr char operator[](std::size_t n) const { return n < sz ? p[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz; } }; // C++11 constexpr functions had to put everything in a single return statement // (C++14 does not have that requirement) constexpr std::size_t countlower(conststr s, std::size_t n = 0, std::size_t c = 0) { return n == s.size() ? c : 'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) : countlower(s, n + 1, c); } // An output function that requires a compile-time constant, for testing template<int n> struct constN { constN() { std::cout << n << '\n'; } }; int main() { std::cout << "4! = "; constN<factorial(4)> out1; // computed at compile time volatile int k = 8; // disallow optimization using volatile std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time std::cout << "The number of lowercase letters in \"Hello, world!\" is "; constN<countlower("Hello, world!")> out2; // implicitly converted to conststr constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; constexpr int length_a = sizeof a / sizeof(int); // std::size(a) in C++17, // std::ssize(a) in C++20 std::cout << "Array of length " << length_a << " has elements: "; for (int i = 0; i < length_a; ++i) std::cout << a[i] << ' '; std::cout << '\n'; }
出力
4! = 24 8! = 40320 The number of lowercase letters in "Hello, world!" is 9 Array of length 12 has elements: 0 1 2 3 4 5 6 7 8 0 0 0
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 1358 | C++11 | テンプレートconstexpr関数も必要とされた 少なくとも1つの有効な引数値を持つ |
必要なし |
| CWG 1359 | C++11 | constexpr共用体コンストラクタ すべてのデータメンバーを初期化する必要がある |
正確に1つのデータを初期化する 非空の共用体のメンバ |
| CWG 1366 | C++11 | constexprコンストラクタを持つクラスの関数本体 が= defaultまたは= deleteである場合、仮想基底クラスを持つことができた |
そのようなクラスはどちらもできない 仮想基底クラスを持つ |
| CWG 1595 | C++11 | constexpr委譲コンストラクタが必要とされた すべての関連するコンストラクタがconstexprであること |
ターゲットのみが必要とされる コンストラクタはconstexprであること |
| CWG 1712 | C++14 | constexpr変数テンプレートは そのすべての宣言にconstexpr指定子が含まれていること[1] |
もう必要ない |
| CWG 1911 | C++11 | 非リテラル型のconstexprコンストラクタは許可されなかった | 定数初期化で許可されるようになった |
| CWG 2004 | C++11 | 可変メンバを持つ共用体のコピー/ムーブ 定数式で許可された |
可変バリアントは対象外となる 暗黙的なコピー/ムーブ |
| CWG 2022 | C++98 | 同等のconstexprと非constexpr関数が 同じ結果を生成するかどうかは コピー省略が実行されるかどうかに依存する |
定数式では常にコピー省略が 実行されると仮定する |
| CWG 2163 | C++14 | ラベルはconstexpr関数で許可されていた goto文が禁止されているにもかかわらず |
ラベルも禁止 |
| CWG 2268 | C++11 | 可変メンバーを持つ共用体のコピー/ムーブは CWG issue 2004の解決によって禁止された |
オブジェクトが作成された場合に許可される 定数式内で |
| CWG 2278 | C++98 | CWG issue 2022の解決は実装不可能だった | コピー省略は決して行われないと仮定する 実行されると仮定する |
| CWG 2531 | C++11 | 非インライン変数がインラインになった constexprで再宣言された場合 |
変数はインラインにならない インラインにならない |
- ↑ constexpr指定子を持つ変数テンプレートの宣言は複数存在しないため、これは冗長です。
[編集] 関連項目
| ない | コンパイル時に評価可能な式を定義する |
consteval 指定子(C++20) |
関数が即時関数であることを指定する。つまり、関数へのすべての呼び出しは定数評価内で行われなければならない。 |
constinit 指定子(C++20) |
変数が静的初期化、すなわちゼロ初期化と定数初期化を持つことを表明する。 |
| C ドキュメント (constexpr)
| |