constexpr 指定子 (C++11以上)
constexpr
ー 変数または関数の値が定数式内に現れることができることを指定します。
目次 |
[編集] 説明
constexpr
指定子は関数または変数の値をコンパイル時に評価できることを宣言します。 そのような変数および関数はコンパイル時定数式のみが使用できる場所で使用できます (適切な引数が与えられれば)。 オブジェクトの宣言または非静的メンバ関数 (C++14未満)で使用された constexpr 指定子は const を暗に含みます。 関数または静的メンバ変数 (C++17以上)の宣言で使用された constexpr は inline を暗に含みます。 ある関数または関数テンプレートのいずれかの宣言が constexpr
指定子を持つ場合は、すべての宣言がその指定子を含まなければなりません。
constexpr 変数は以下の要件を満たさなければなりません。
- その型は LiteralType でなければなりません。
- 直ちに初期化されなければなりません。
- その初期化の完全式 (すべての暗黙の変換やコンストラクタ呼び出しなどを含みます) は定数式でなければなりません。
constexpr 関数は以下の要件を満たさなければなりません。
|
(C++20未満) |
- その戻り値の型は LiteralType でなければなりません。
- そのすべての引数は LiteralType でなければなりません。
- 関数の呼び出しがコア定数式の評価された部分式 (コンストラクタの場合は、定数初期化子での使用で十分です) (C++14以上)となるような実引数の集合が少なくともひとつは存在する。 この要件の違反に対して診断は要求されません。
(C++14未満) | |||
|
(C++14以上) |
constexpr コンストラクタは以下の要件を満たさなければなりません。
- そのすべての引数は LiteralType でなければなりません。
- クラスは仮想基底クラスを持ってはなりません。
|
(C++20未満) |
|
(C++14未満) |
|
(C++14以上) |
- クラスまたは構造体のコンストラクタの場合は、すべての基底クラス部分オブジェクトおよびすべての変種でない非静的データメンバは初期化されなければなりません。 クラスが union ライクなクラスの場合は、空でない無名の union メンバのそれぞれについて、ちょうど1個の変種メンバが初期化されなければなりません。
- 空でない union のコンストラクタの場合は、ちょうど1個の非静的データメンバが初期化されなければなりません。
- 非静的メンバおよび基底クラスを初期化するために選択されたすべてのコンストラクタは constexpr でなければなりません。
constexpr 関数テンプレートおよびクラステンプレートの constexpr メンバ関数の場合は、少なくとも1個の特殊化は上記の要件を満たさなければなりません。 他の特殊化は、たとえそのような関数の呼び出しが定数式に現れることができなくとも、 constexpr とみなされます。
[編集] ノート
noexcept 演算子は定数式に対しては必ず constexpr int f(); constexpr bool b1 = noexcept(f()); // false、未定義の constexpr 関数。 constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true、 f() は定数式です。 |
(C++17未満) |
constexpr コンストラクタはリテラル型でないクラスに対しても使用できます。 例えば std::unique_ptr のデフォルトコンストラクタは constexpr であり、これによって定数初期化が可能となります。
参照変数は constexpr 宣言できます (初期化子は参照定数式である必要があります)。
static constexpr int const& x = 42; // const int オブジェクトへの constexpr 参照 // (static な参照による生存期間延長のため // このオブジェクトは静的記憶域期間を持ちます)。
constexpr 関数内で try ブロックおよびインラインアセンブリは許されていますが、定数式内で例外を投げることやアセンブリを実行することは未だ許されていません。 |
(C++20以上) |
[編集] キーワード
[編集] 例
階乗を計算する C++11 の constexpr 関数と文字列リテラルを拡張するリテラル型の定義。
#include <iostream> #include <stdexcept> // C++11 の constexpr 関数は繰り返しではなく再帰を使用します // (C++14 の constexpr 関数はローカル変数やループを使用できます)。 constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // リテラルクラス。 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 関数は例外を投げることによってエラーを知らせます。 // C++11 では条件演算子 ?: でそれを行わなければなりません。 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 関数は単一の return 文にすべてを詰め込む必要がありました // (C++14 にはその要件はありません)。 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); } // テスト用のコンパイル時定数を要求する出力関数。 template<int n> struct constN { constN() { std::cout << n << '\n'; } }; int main() { std::cout << "4! = " ; constN<factorial(4)> out1; // コンパイル時に計算されます。 volatile int k = 8; // volatile を用いて最適化を妨げます。 std::cout << k << "! = " << factorial(k) << '\n'; // 実行時に計算されます。 std::cout << "the number of lowercase letters in \"Hello, world!\" is "; constN<countlower("Hello, world!")> out2; // conststr に暗黙に変換されます。 }
出力:
4! = 24 8! = 40320 the number of lowercase letters in "Hello, world!" is 9
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 1911 | C++14 | constexpr constructors for non-literal types were not allowed | allowed in constant initialization |
CWG 2004 | C++14 | copy/move of a union with a mutable member was allowed in a constant expression | mutable variants disqualify implicit copy/move |
CWG 2163 | C++14 | labels were allowed in constexpr functions even though gotos are prohibited | labels also prohibited |
CWG 2268 | C++14 | copy/move of a union with a mutable member was prohibited by cwg 2004 | allowed if the object was created within the constant expression |