名前空間
変種
操作

定数式

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (C++20未満)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 

コンパイル時に評価できるを定義します。

そのような式は、非型テンプレート引数、配列のサイズ、およびその他の定数式を要求する文脈で使用できます。 例えば、

int n = 1;
std::array<int, n> a1; // エラー、 n は定数式ではありません。
const int cn = 2;
std::array<int, cn> a2; // OK、 cn は定数式です。

目次

[編集] コア定数式

コア定数式は、その評価が以下のいずれも評価しないであろう任意の式です。

1) this ポインタ。 ただし式の一部として評価されている constexpr 関数 (または constexpr コンストラクタ) 内の場合は除きます。
2) constexpr 宣言されていない関数 (またはコンストラクタ) を呼ぶ関数呼び出し式。
constexpr int n = std::numeric_limits<int>::max(); // OK、 max() は constexpr です。
constexpr int m = std::time(nullptr); // エラー、 std::time() は constexpr ではありません。
3) 宣言されているけれども定義されていない constexpr 関数への関数呼び出し。
4) 実体化が constexpr 関数 (またはコンストラクタ) の要件を満たさない constexpr 関数 (またはコンストラクタ) テンプレート実体化への関数呼び出し。
5) 処理系定義の制限を超えるであろう式。
6) その評価が何らかの形式のコア言語の 未定義動作に繋がる式 (符号付き整数のオーバーフロー、ゼロ除算、配列の境界を超えるポインタ算術、など)。 標準ライブラリの未定義動作が検出されるかどうかは未規定です。
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未満)
8) 左辺値から右辺値への暗黙の変換。 ただし左辺値が以下の場合は除きます。
  • 整数型または列挙型であり、定数式で初期化された非 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 オブジェクトを参照する。
9) 左辺値から右辺値への暗黙の変換または共用体の非アクティブメンバに適用される変更またはその部分オブジェクト (たとえそれがアクティブメンバと共通先頭列を共有していても)。
10) アクティブメンバ (もしあれば) が変更可能な共用体に対する暗黙に定義されたコピー/ムーブコンストラクタまたはコピー/代入演算子の呼び出し。 ただしその共用体オブジェクトの生存期間がこの式の評価中に始まる場合は除きます。
11) 共用体のアクティブメンバを変更するであろう代入式またはオーバーロード代入演算子の呼び出し。
(C++17以上)
(C++20未満)
12) 参照型の変数またはデータメンバを参照する id-expression。 ただし定数式で初期化されたか、その生存期間がこの式の評価中に始まる場合は除きます。
13) 述語が false に評価されるチェック付き契約
(C++20以上)
14) cv void* からあらゆるオブジェクトポインタ型への変換。
(C++20未満)
17) 疑似デストラクタ呼び出し。
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未満)
21) new 式または delete 式。
22) 結果が未規定なときの三方比較
(C++20以上)
23) 結果が未規定なときの等しい演算子または関係演算子。
24) 代入または複合代入演算子。
(C++14未満)
25) throw 式。
26) 例外を投げるであろう dynamic_cast または typeid 式。
(C++20以上)
27) ラムダ式の内側において、 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以上)

ノート: コア定数式であるというだけでは、いかなる直接的な意味も持ちません。 式は特定の文脈で使用するためには以下のサブセットのいずれかでなければなりません。

[編集] 整数定数式

整数定数式は、 prvalue に暗黙に変換された、その変換後の式がコア定数式である、整数型またはスコープなし列挙型の式です。 整数定数式が期待される場面でクラス型の式が使用される場合、その式は整数型またはスコープなし列挙型に文脈的に暗黙に変換されます。

以下の文脈が整数定数式を要求します。

(C++14未満)
  • ヌルポインタ定数。
(C++14未満)

[編集] 変換された定数式

T 型の変換された定数式は、その変換後の式が定数式であり、その暗黙の変換シーケンスが以下のみを含む、 T 型に暗黙に変換された式です。

  • constexpr ユーザ定義変換 (そのため整数型が期待されている場面でクラスを使用できます)。
  • 左辺値から右辺値への変換。
  • 整数昇格。
  • 縮小でない整数変換。
  • 配列からポインタへの変換。
  • 関数からポインタへの変換。
  • 関数ポインタ変換 (noexcept 関数へのポインタから関数へのポインタへの変換)
  • 修飾変換。
  • std::nullptr_t からのヌルポインタ変換。
  • std::nullptr_t からのヌルメンバポインタ変換。
(C++17以上)
  • また、何らかの参照の束縛が行われた場合、それは直接束縛です (一時オブジェクトを構築するものではありません)。

以下の文脈が変換された定数式を要求します。

(C++14以上)

bool 型に文脈的に変換された定数式は、その変換された式が定数式であり、その変換シーケンスが上記の変換のみを含む、 bool に文脈的に変換された式です。

以下の文脈が bool 型に文脈的に変換された定数式を要求します。

(C++17以上)
(C++20以上)

[編集] 定数式

定数式は、以下のいずれかです。

  • 以下のいずれかを参照する glvalue のコア定数式
  • 一時オブジェクトでない静的記憶域期間を持つオブジェクト。
  • 下の prvalue に対する制約を満たす値を持つ、一時オブジェクトである静的記憶域期間を持つオブジェクト。
  • 即時でない (C++20以上)関数。
  • 以下の制約を満たす値を持つ、 prvalue のコア定数式
  • 値がクラス型のオブジェクトの場合は、すべての参照型の非静的データメンバが上の glvalue に対する制約を満たすエンティティを参照する。
  • 値がポインタ型の場合は、以下のいずれかを保持する。
  • 静的記憶域期間を持つオブジェクトのアドレス。
  • 静的記憶域期間を持つオブジェクトの最後の次のアドレス。
  • 即時でない (C++20以上)関数のアドレス。
  • ヌルポインタ値。
  • 値がメンバ関数へのポインタ型の場合は、即時関数を指さない。 (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 として宣言することは許されません。

名前付き戻り値の最適化 (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) [edit]
(C++11)(C++17で非推奨)(C++20で削除)
型がリテラル型かどうか調べます
(クラステンプレート) [edit]