web-dev-qa-db-ja.com

constexprが本当にconstexprであるかを知ることは可能ですか?

constexprの拡張バージョン(C++ 14からだと思います)なので、「実際の」constexprとして使用できるconstexpr関数を宣言できます。つまり、コードコンパイル時に実行されるか、インライン関数として動作します。だから、このプログラムを持つことができるとき:

_#include <iostream>

constexpr int foo(const int s) {
  return s + 4;
}

int main()
{
    std::cout << foo(3) << std::endl;
    const int bar = 3;
    std::cout << foo(bar) << std::endl;
    constexpr int a = 3;
    std::cout << foo(a) << std::endl;

    return 0;
}
_

結果は次のとおりです。

_7
7
7
_

ここまでは順調ですね。

関数がコンパイル時または実行時に実行されるかどうかをfoo(const int s)で知る方法(おそらく標準)はありますか?

編集:関数がコンパイル時に評価されたかどうかを実行時に知ることも可能ですか?

25
LeDYoM

記載されている手法は機能しますが、static_assert sfinaeフレンドリーではありません。これを行うためのより良い方法(理論的には、私の意味がわかります)は、関数がnoexceptであるかどうかを確認することです。どうして?なぜなら、関数がそのようにマークされていなくても、定数式は常に例外ではないからです。したがって、次のコードを検討してください。

template <class T>
constexpr void test_helper(T&& t) {}

#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))

test_helperconstexprであるため、引数がある限り、定数式になります。定数式の場合はnoexceptになりますが、それ以外の場合はそうなりません(そのようにマークされていないため)。

それでは、これを定義しましょう:

double bar(double x) { return x; }

constexpr double foo(double x, bool b) {
    if (b) return x; 
    else return bar(x);
}

fooが定数式であり、noexceptがtrueの場合、xbのみです。ブール値がfalseの場合、constexpr以外の関数を呼び出し、constexpr-nessを台無しにします。それで、これをテストしましょう:

double d = 0.0;

constexpr auto x = IS_CONSTEXPR(foo(3.0, true));
constexpr auto y = IS_CONSTEXPR(foo(3.0, false));
constexpr auto z = IS_CONSTEXPR(foo(d, true));

std::cerr << x << y << z;

コンパイルできました。これにより、コンパイル時のブール値(コンパイルエラーではなく)が得られます。たとえば、sfinaeに使用できます。

キャッチ?まあ、clangには複数年のバグがあり、これを正しく処理していません。ただし、gccにはあります。ライブの例: http://coliru.stacked-crooked.com/a/e7b037932c358149 。本来の「100」を出力します。

22
Nir Friedman

C++ 20では、ヘッダーis_constant_evaluatedで定義されている<type_traits>が導入され、この問題に対処しています。

constexpr int foo(int s)
{
    if (std::is_constant_evaluated()) // note: not "if constexpr"
        /* evaluated at compile time */;
    else
        /* evaluated at run time */;
}

ここでは、if constexprの代わりに通常のifが使用されていることに注意してください。 if constexprを使用する場合は、コンパイル時に条件を評価する必要があるため、is_constant_evaluatedは常にtrueを返し、テストが役に立たなくなります。

9
L. F.

それを行うための正規の方法はstatic_assertstatic_assertsはコンパイル時に評価されるため、条件がfalseの場合はビルドが中断されます。

#include <iostream>

constexpr int foo(const int s) {
  return s + 4;
}

int main()
{
    std::cout << foo(3) << std::endl;
    const int bar = 3;
    std::cout << foo(bar) << std::endl;
    constexpr int a = 3;
    std::cout << foo(a) << std::endl;

    static_assert(foo(3) == 7, "Literal failed");
    static_assert(foo(bar) == 7, "const int failed");
    static_assert(foo(a) == 7, "constexpr int failed");
    return 0;
}

clang++ -std=c++14 so1.cppは問題なくコンパイルされ、すべてが期待どおりに機能することを示しています。

5
peteigel

constexpr関数内では、constexprコンテキストで評価されているかどうかはわかりません。この機能を追加するための多くの提案があります。 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0595r2.html が成功しました。 c ++ 20 に出現し、 c ++ 21


constexpr関数の外では、特定の引数のセットを持つ関数の呼び出しがconstexprコンテキストで評価されるかどうかを判断する方法がいくつかあります。最も簡単なのは、constexprを必要とするコンテキストで結果を使用することです。

Constexpr式が非void整数型またはポインター型(関数ポインターを含む)を返すと仮定します。

_#define CONSTEXPR_EVAL(...) \
  std::integral_constant< \
    std::decay_t<decltype(__VA_ARGS__)>, \
    __VA_ARGS__ \
  >::value
_

CONSTEXPR_EVAL( bar(foo, true) )は、bar(foo, true)がコンパイル時に評価できない場合はコンパイルに失敗し、コンパイル時に評価できる場合はその値を返します。

noexcept(コンパイル時に評価される関数はnoexcept)を含む他のトリックが機能します( @ NirFriedmanの回答 を参照) 。

パーティーを台無しにして申し訳ありませんが、確かに標準のやり方はありません。 as-ifルールでは、コンパイル時に別のコンテキストで強制的に計算するように強制されている場合でも、コンパイラーは実行時に結果を計算するコードを発行できます。コンパイル時に実行できることは、実行時に再度実行できますか?そして、計算はスローしないことがすでに証明されています。

拡張により、標準に準拠したIS_REALLY_CONSTEXPRまたはis_really_constexpr checkは、まったく同じ呼び出し、またはさらに言えば、まったく同じconstexprシンボルの値が実行時間の計算に関係することを証明できません。

もちろん、通常は実行時に計算を繰り返す理由はありませんが、コンパイル時にすでに実行されている場合もありませんが、問題は、コンパイラが事前計算された結果を使用するかどうかを伝えることでした。 。

おそらく標準と言ったので、実際には、提供されたソリューションの1つを選択したコンパイラでテストし、それが一貫して動作することを期待するのがおそらく最善の策です。 (または、オープン/パブリックソースであり、その傾向がある場合は、ソースコードを読み取ります。)

0
Arne Vogel