11

数日前、コンパイラがコンパイル時に constexpr 関数を計算するかどうかを決定する基準を尋ねました。

コンパイル時に 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;
}

template<typename T>
void foobar(T val)
{
    std::cout << val << std::endl;
}

int main(int argc, char** argv)
{
    foobar(POW((unsigned long long)2, 63));
    return 0;
}

私が言われたことが本当なら、このコード例は非常に実用的ではありません.コンパイル時に計算します。コンパイル時の評価を強制するための明白な解決策は次のとおりです。

auto expr = POW((unsigned long long)2, 63);
foobar(expr);

ただし、これにより、コンパイル時に constexpr が確実に評価されるようにするたびに、追加のコード行を使用する必要がなくなります。これをもう少し便利にするために、次の疑わしいマクロを考え出しました。

#define FORCE_CT_EVAL(func) [](){constexpr auto ___expr = func; return std::move(___expr);}()
foobar(FORCE_CT_EVAL(POW((unsigned long long)2, 63)));

正常に動作しているにもかかわらず、何かがおかしいように感じます。匿名ラムダの作成はパフォーマンスに影響しますか? 右辺値参照によって返されると、実際に式が関数パラメーターに移動しますか? std::move はパフォーマンスにどのように影響しますか? これに対するより良いワンライナーソリューションはありますか?

4

1 に答える 1

5

コメントに埋もれたままにしないように:

#include <type_traits>

#define COMPILATION_EVAL(e) (std::integral_constant<decltype(e), e>::value)

constexpr int f(int i){return i;}

int main()
{
    int x = COMPILATION_EVAL(f(0));
}

EDIT1:

このアプローチの注意点として、constexpr関数は浮動小数点を受け入れて浮動constexpr小数点変数に割り当てることができますが、浮動小数点型を型以外のテンプレート パラメーターとして使用することはできません。また、他の種類のリテラルにも同じ制限があります。

constexprあなたのラムダはそれでうまくいきますが、非ものが関数に渡されたときに意味のあるエラーメッセージを取得するには、 default-capture が必要だと思います。あの結末std::moveは必見です。

EDIT2:

エラー、あなたのラムダアプローチは私にはうまくいきません。ラムダはconstexpr関数ではありません。それはあなたにとってもうまくいかないはずです。

それを回避する方法は本当にないようconstexprですが、ローカルスコープで変数を初期化します。

EDIT3:

ああ、わかりました、悪いですが、ラムダの目的は単なる評価です。だからそれはそのために働いています。代わりに、その結​​果は、別のコンパイル時間の評価に従うには使用できません。

EDIT4:

C++17 では、コンテキストでラムダを使用できるようconstexprになったため、EDIT2/EDIT3 で言及されている制限が削除されました。したがって、ラムダソリューションは正しいものです。詳細については、このコメントを参照してください。

于 2013-01-12T19:18:26.643 に答える