定数式
コンパイル時に評価できる式を定義します。
そのような式は、非型テンプレート引数、配列のサイズ、およびその他の定数式を要求する文脈で使用できます。 例えば、
int n = 1; std::array<int, n> a1; // エラー、 n は定数式ではありません。 const int cn = 2; std::array<int, cn> a2; // OK、 cn は定数式です。
目次 |
[編集] コア定数式
コア定数式は、その評価が以下のいずれも評価しないであろう任意の式です。
-
this
ポインタ。 ただし式の一部として評価されているconstexpr
関数 (またはconstexpr
コンストラクタ) 内の場合は除きます。 - constexpr 宣言されていない関数 (またはコンストラクタ) を呼ぶ関数呼び出し式。
constexpr int n = std::numeric_limits<int>::max(); // OK、 max() は constexpr です。 constexpr int m = std::time(nullptr); // エラー、 std::time() は constexpr ではありません。
- 宣言されているけれども定義されていない
constexpr
関数への関数呼び出し。 - 実体化が constexpr 関数 (またはコンストラクタ) の要件を満たさない
constexpr
関数 (またはコンストラクタ) テンプレート実体化への関数呼び出し。 - (C++20以上) 生存期間がその式の外側で始まる、���数式内で使用可能 (後述) でないオブジェクトに対して呼ばれた、 constexpr 仮想関数の関数呼び出し。
- 処理系定義の制限を超えるであろう式。
- その評価が何らかの形式のコア言語の 未定義動作に繋がる式 (符号付き整数のオーバーフロー、ゼロ除算、配列の境界を超えるポインタ算術、など)。 標準ライブラリの未定義動作が検出されるかどうかは未規定です。
constexpr double d1 = 2.0/1.0; // OK。 constexpr double d2 = 2.0/0.0; // エラー (未定義)。 constexpr int n = std::numeric_limits<int>::max() + 1; // エラー (オーバーフロー)。 int x, y, z[30]; constexpr auto e1 = &y - &x; // エラー (未定義)。 constexpr auto e2 = &z[20] - &z[3]; // OK。 constexpr std::bitset<2> a; constexpr bool b = a[2]; // 未定義 (検出される場合は未規定)。
- (C++17未満)ラムダ式。
- 左辺値から右辺値への暗黙の変換。 ただし以下の場合は除きます。
- 定数式で使用可能 (後述) なオブジェクトを指示する非 volatile な glvalue に適用された。
int main() { const std::size_t tabsize = 50; int tab[tabsize]; // OK、 tabsize は const 修飾された整数型であり、 // その初期化子は定数初期化子であるため、 // tabsize は定数式で使用可能であるため、 // tabsize は定数式です。 std::size_t n = 50; const std::size_t sz = n; int tab2[sz]; // エラー、 sz の初期化子は定数初期化子でないため、 // sz は定数式で使用可能でないため、 // sz は定数式ではありません。 }
- または、生存期間がこの式の評価中に始まる非 volatile オブジェクトを参照するリテラル型の非 volatile な glvalue に適用された。
- 定数式で使用可能 (後述) なオブジェクトを指示する非 volatile な glvalue に適用された。
- 左辺値から右辺値への暗黙の変換または共用体の非アクティブメンバに適用される変更またはその部分オブジェクト (たとえそれがアクティブメンバと共通先頭列を共有していても)。
- アクティブメンバ (もしあれば) が変更可能な共用体に対する暗黙に定義されたコピー/ムーブコンストラクタまたはコピー/代入演算子の呼び出し。 ただしその共用体オブジェクトの生存期間がこの式の評価中に始まる場合は除きます。
- (C++17以上) (C++20未満) 共用体のアクティブメンバを変更するであろう代入式またはオーバーロード代入演算子の呼び出し。
- 参照型の変数またはデータメンバを参照する id-expression。 ただしその参照が定数式で使用可能 (後述) であるか、その生存期間がこの式の評価中に始まる場合は除きます。
- cv void* からあらゆるオブジェクトポインタ型への変換。
- (C++20未満)
dynamic_cast
。 -
reinterpret_cast
。 - 疑似デストラクタ呼び出し。
- (C++14未満) インクリメントまたはデクリメント演算子。
-
(C++14以上) オブジェクトの変更。 ただしオブジェクトが非 volatile リテラル型であり、その生存期間がこの式の評価中に始まる場合は除きます。
constexpr int incr(int& n) { return ++n; } constexpr int g(int k) { constexpr int x = incr(k); // エラー、 k の生存期間は式 incr(k) の外で始まるため // incr(k) はコア定数式ではありません。 return x; } constexpr int h(int k) { int x = incr(k); // OK、 x はコア定数式で初期化されることは要求されません。 return x; } constexpr int y = h(1); // OK、 y を値 2 で初期化します。 // k の生存期間は式 h(1) の中で始まるため // h(1) はコア定数式です。
- (C++20未満) 多相型の glvalue に適用される
typeid
式。 }} - new 式または delete 式。
- (C++20以上) 結果が未規定なときの三方比較。
- 結果が未規定なときの等しい演算子または関係演算子。
- (C++14未満) 代入または複合代入演算子。
- throw 式。
- (C++20以上) 例外を投げるであろう
dynamic_cast
またはtypeid
式。 - ラムダ式の内側において、
this
またはそのラムダの外側で定義された変数への参照 (その参照が ODR 使用であろう場合)。void g() { const int n=0; constexpr int j=*&n; // OK、ラムダ式の外側。 [=]{ constexpr int i=n; // OK、ここでは 'n' は ODR 使用ではなく、キャプチャされません。 constexpr int j=*&n;// ill-formed、 '&n' は 'n' の ODR 使用です。 }; }
クロージャへの関数呼び出し内で ODR 使用が行われる場合は、それは代わりにクロージャのデータメンバにアクセスするため、
this
や囲っている変数を参照しないことに注意してください。// OK、「v」および「m」は ODR 使用ですが、ネストしたラムダ内の定数式内では発生しません。 auto monad = [](auto v){return [=]{return v;};}; auto bind = [](auto m){return [=](auto fvm){return fvm(m());};}; // 定数式評価中に作成された自動オブジェクトへのキャプチャを持つことは OK です。 static_assert(bind(monad(2))(monad)() == monad(2)());
(C++17以上)
This section is incomplete Reason: needs more mini-examples and less standardese |
ノート: コア定数式であるというだけでは、いかなる直接的な意味も持ちません。 式は特定の文脈で使用するためには以下のサブセットのいずれかでなければなりません。
[編集] 定数式で使用可能
上の一覧において、以下のいずれかを満たす場合、その変数は定数式で使用可能です。
- constexpr 変数である。
- 以下の型の定数初期化された変数である。
- 参照型
- const 修飾された整数型または列挙型
以下のいずれかを満たす場合、そのオブジェクトまたは参照は定数式で使用可能です。
- 定数式で使用可能な変数である。
- (C++20以上) テンプレート引数オブジェクトである。
- 文字列リテラルオブジェクトである。
- 上記のいずれかの非 mutable な部分オブジェクトまたは参照メンバである。
- 定数式で初期化された const 修飾された非 volatile な整数または列挙型の完全一時オブジェクトである。
const std::size sz = 10; // sz は定数式で使用可能です。
[編集] 整数定数式
整数定数式は、 prvalue に暗黙に変換された、その変換後の式がコア定数式である、整数型またはスコープなし列挙型の式です。 整数定数式が期待される場面でクラス型の式が使用される場合、その式は整数型またはスコープなし列挙型に文脈的に暗黙に変換されます。
以下の文脈が整数定数式を要求します。
(C++14未満) |
- ビットフィールドの長さ。
- ベースとなる型が固定されていない列挙の初期化子。
|
(C++14未満) |
[編集] 変換された定数式
T
型の変換された定数式は、その変換後の式が定数式であり、その暗黙の変換シーケンスが以下のみを含む、 T 型に暗黙に変換された式です。
- constexpr ユーザ定義変換 (そのため整数型が期待されている場面でクラスを使用できます)。
- 左辺値から右辺値への変換。
- 整数昇格。
- 縮小でない整数変換。
|
(C++17以上) |
- また、何らかの参照の束縛が行われた場合、それは直接束縛です (一時オブジェクトを構築するものではありません)。
以下の文脈が変換された定数式を要求します。
(C++14以上) |
- 整数および列挙の (C++17未満)非型テンプレート引数。
bool 型に文脈的に変換された定数式は、その変換された式が定数式であり、その変換シーケンスが上記の変換のみを含む、 bool に文脈的に変換された式です。
以下の文脈が bool 型に文脈的に変換された定数式を要求します。
(C++17以上) |
(C++20以上) |
[編集] 定数式
定数式は、以下のいずれかです。
|
(C++14以上) | ||
定数式は、リテラル定数式、参照定数式、またはアドレス定数式です。 リテラル定数式は、非ポインタリテラル型の prvalue のコア定数式 (文脈によって要求される変換後) です。 配列またはクラス型のリテラル定数式は、それぞれの部分オブジェクトが定数式で初期化されることを要求します。 参照定数式は静的記憶域期間を持つオブジェクトまたは関数を指す lvalue のコア定数式です。 アドレス定数式は、静的記憶域期間を持つオブジェクト、静的記憶域期間を持つ配列の最後の次、関数を指す、またはヌルポインタである、 std::nullptr_t またはポインタ型の prvalue のコア定数式 (文脈によって要求される変換後) です。 |
(C++14未満) |
void test() { static const int a = std::random_device{}(); constexpr const int& ra = a; // OK、 a は glvalue の定数式です。 constexpr int ia = a; // エラー、 a は prvalue の定数式ではありません。 const int b = 42; constexpr const int& rb = b; // エラー、 b は glvalue の定数式ではありません。 constexpr int ib = b; // OK、 b は prvalue の定数式です。 }
[編集] ノート
標準が 名前付き戻り値の最適化 (NRVO) は定数式では許されないのに対して、戻り値の最適化 (RVO) は必須です。 |
(C++14以上) |
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 2167 | C++14 | non-member references local to an evaluation were making the evaluation non-constexpr | non-member references allowed |
CWG 1313 | C++11 | undefined behavior was permitted, and all pointer subtraction was prohibited | same-array pointer subtraction ok, UB prohibited |
CWG 1952 | C++11 | standard library undefined behavior was required to be diagnosed | unspecified whether library UB is diagnosed |
[編集] 関連項目
constexpr 指定子(C++11)
|
変数または関数の値がコンパイル時に計算できることを指定します |
(C++11)(C++17で非推奨)(C++20で削除) |
型がリテラル型かどうか調べます (クラステンプレート) |