web-dev-qa-db-ja.com

定数整数と定数評価

次のプログラムを検討してください。

#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を出力します (ワンドボックス)。代わりにコンパイルエラーが発生しなかったのはなぜですか?

38
Pilar Latiesa

定数評価は楽しいじゃないですか?

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_で初期化されます。しかし、特に、inotこの時点では定数式です。 iの初期化子がnot定数式だったため、後続のstatic_assert(i == 1)は不正な形式になります!非定数の初期化パスが(そうでなければ)完全に定数式の要件を満たしている場合でも、.


試した場合:

_constexpr int i = f();
_

非定数の初期化にフォールバックできないため、これは失敗しました。

38
Barry