web-dev-qa-db-ja.com

constexpr関数はいつコンパイル時に評価されますか?

Constexprとして宣言された関数が実行時に呼び出される可能性があるので、コンパイラーはどの基準でコンパイル時または実行時にそれを計算するかを決定しますか?

template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
    return (expo != 0 )? base * POW(base, expo -1) : 1;
}

int main(int argc, char** argv)
{
    int i = 0;
    std::cin >> i;

    std::cout << POW(i, 2) << std::endl;
    return 0;
}

この場合、コンパイル時にはiは不明です。これが、コンパイラがPOW()を実行時に呼び出される通常の関数として扱う理由です。ただし、この動的な動作は、見た目と同じくらい便利な場合もあり、実際的ではありません。たとえば、コンパイラーがconstexpr関数をコンパイル時に計算して、コンパイラーが通常の関数として扱うことを決定した場合、コンパイラーがコンパイル時にも機能していた場合はどうでしょうか?既知の一般的な落とし穴はありますか?

61
Byzantian

constexpr関数willは、その引数がすべて定数式であり、結果が定数式でも使用されている場合に、コンパイル時に評価されます。定数式は、リテラル(42など)、タイプでないテンプレート引数(template<class T, size_t N> class array;内のNなど)、enum要素宣言(enum Color { Red, Blue, Green };内のBlueなど)、宣言された別の変数にすることができますconstexprなど。

それらのmightは、すべての引数が定数式であり、結果がnotが定数式で使用されている場合に評価されますが、実装次第です。

82
K-ballo

定数式が必要な場合、関数はコンパイル時に評価する必要があります。

これを保証する最も簡単な方法は、constexpr値またはstd::integral_constantを使用することです。

constexpr auto result = POW(i, 2); // this should not compile since i is not a constant expression
std::cout << result << std::endl;

または:

std::cout << std::integral_constant<int, POW(i, 2)>::value << std::endl;

または

#define POW_C(base, power) (std::integral_constant<decltype(POW((base), (power)), POW((base), (power))>::value)

std::cout << POW_C(63, 2) << std::endl;

または

template<int base, int power>
struct POW_C {
  static constexpr int value = POW(base, power);
};

std::cout << POW_C<2, 63>::value << std::endl;
19
Pubby