定数式
コンパイル時に評価できる式を定義します。
そのような式は、非型テンプレート引数、配列のサイズ、およびその他の定数式を要求する文脈で使用できます。 例えば、
int n = 1; std::array<int, n> a1; // エラー、 n は定数式ではありません。 const int cn = 2; std::array<int, cn> a2; // OK、 cn は定数式です。
目次 |
[編集] コア定数式
コア定数式は、その評価が以下のいずれも評価しないであろう任意の式です。
constexpr int n = std::numeric_limits<int>::max(); // OK、 max() は constexpr です。 constexpr int m = std::time(nullptr); // エラー、 std::time() は constexpr ではありません。
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]; // 未定義 (検出される場合は未規定)。
7) ラムダ式。
|
(C++17未満) |
- 整数型または列挙型であり、定数式で初期化された非 volatile かつ const な完全オブジェクトを参照する。
int main() { const std::size_t tabsize = 50; int tab[tabsize]; // OK、 tabsize は定数式です。 std::size_t n = 50; const std::size_t sz = n; int tab2[sz]; // エラー、 sz は定数式ではありません // (sz が定数式で初期化されていないため)。 }
- 文字列リテラルの要素を参照する非 volatile な glvalue である。
- リテラル型であり、
constexpr
付きで定義された非 volatile オブジェクトまたはテンプレート引数オブジェクト (C++20以上)またはその mutable でない部分オブジェクトを参照する。 - リテラル型であり、生存期間がこの式の評価中に始まる非 volatile オブジェクトを参照する。
11) 共用体のアクティブメンバを変更するであろう代入式またはオーバーロード代入演算子の呼び出し。
|
(C++17以上) (C++20未満) |
(C++20以上) |
15) dynamic_cast。
|
(C++20未満) |
18) インクリメントまたはデクリメント演算子。
|
(C++14未満) |
19) オブジェクトの変更。 ただしオブジェクトが非 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++14以上) |
20) 多相型の glvalue に適用される typeid 式。
|
(C++20未満) |
22) 同じ完全型またはその何らかの部分オブジェクトを指さないポインタを比較する三方比較。
|
(C++20以上) |
24) 代入または複合代入演算子。
|
(C++14未満) |
(C++20以上) |
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 使用が行われる場合は、それは代わりにクロージャのデータメンバにアクセスするため、 // 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 |
ノート: コア定数式であるというだけでは、いかなる直接的な意味も持ちません。 式は特定の文脈で使用するためには以下のサブセットのいずれかでなければなりません。
[編集] 整数定数式
整数定数式は、 prvalue に暗黙に変換された、その変換後の式がコア定数式である、整数型またはスコープなし列挙型の式です。 整数定数式が期待される場面でクラス型の式が使用される場合、その式は整数型またはスコープなし列挙型に文脈的に暗黙に変換されます。
以下の文脈が整数定数式を要求します。
(C++14未満) |
- ビットフィールドの長さ。
- ベースとなる型が固定されていない列挙の初期化子。
|
(C++14未満) |
[編集] 変換された定数式
T
型の変換された定数式は、その変換後の式が定数式であり、その暗黙の変換シーケンスが以下のみを含む、 T 型に暗黙に変換された式です。
- constexpr ユーザ定義変換 (そのため整数型が期待されている場面でクラスを使用できます)。
- 左辺値から右辺値への変換。
- 整数昇格。
- 縮小でない整数変換。
|
(C++17以上) |
- また、何らかの参照の束縛が行われた場合、それは直接束縛です (一時オブジェクトを構築するものではありません)。
以下の文脈が変換された定数式を要求します。
(C++14以上) |
- 整数および列挙の (C++17未満)非型テンプレート引数。
bool 型に文脈的に変換された定数式は、その変換された式が定数式であり、その変換シーケンスが上記の変換のみを含む、 bool に文脈的に変換された式です。
以下の文脈が bool 型に文脈的に変換された定数式を要求します。
(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 の定数式です。 }
[編集] ノート
標準が constexpr であると言っていない限り、処理系はライブラリ関数を constexpr として宣言することは許されません。
コピー省略は定数式では必須です。 |
(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 |