次のプログラムを検討してください。
#include <iostream>
#include <type_traits>
constexpr int f() {
if (std::is_constant_evaluated())
return -1;
else return 1;
}
int main() {
int const i = f();
std::cout << i;
}
It prints -1
実行時 (ワンドボックス)。
ただし、コンパイル時に評価されるときに関数throw
を作成すると、次のようになります。
#include <iostream>
#include <type_traits>
constexpr int f() {
if (std::is_constant_evaluated())
throw -1; // <----------------------- Changed line
else return 1;
}
int main() {
int const i = f();
std::cout << i;
}
それ うまくコンパイルして1を出力します (ワンドボックス)。代わりにコンパイルエラーが発生しなかったのはなぜですか?
定数評価は楽しいじゃないですか?
tryで一定の評価を行い、ifが失敗した場合、言語にいくつかの場所があり、不定の評価を行います。静的初期化はそのような場所の1つであり、定数整数の初期化は別の場所です。
何が起こるか:
_int const i = f();
_
これはある可能性がある定数評価ですが、必ずしもそうである必要はありません。 (非constexpr
)定数整数は定数式として引き続き使用できるため、他のすべての条件を満たす場合、試してみる必要があります。例えば:
_const int n = 42; // const, not constexpr
std::array<int, n> arr; // n is a constant expression, this is ok
_
だから試してみましょう-f()
を定数式として呼び出します。このコンテキストでは、std::is_constant_evaluated()
はtrue
であるため、throw
でブランチにヒットし、失敗します。定数評価中はthrow
できないため、定数評価は失敗します。
しかし、次にフォールバックして、再試行します。今回はf()
を非定数式として呼び出します(つまり、std::is_constant_evaluated()
はfalse
)。このパスは成功し、_1
_が与えられるため、i
は値_1
_で初期化されます。しかし、特に、i
はnotこの時点では定数式です。 i
の初期化子がnot定数式だったため、後続のstatic_assert(i == 1)
は不正な形式になります!非定数の初期化パスが(そうでなければ)完全に定数式の要件を満たしている場合でも、.
試した場合:
_constexpr int i = f();
_
非定数の初期化にフォールバックできないため、これは失敗しました。