7

を使用constexprすると、引数に応じて、コンパイル時または実行時に関数を評価できます。ただし、通常、アルゴリズムはコンパイル時と実行時で異なる必要があります。例えば。階乗のconstexprバージョンを考えてみましょう。

constexpr int fact(int n)
{
return (n)?n*fact(n-1):1;
}

実行時に発生した場合n、関数は1つのforループよりも非効率になりませんか?関数がコンパイル時または実行時に実行されているかどうかを判断し、異なるアルゴリズムを使用するためのテンプレートマジックはありますか?

更新
階乗は単なる例でした。すべての関数は、制限constexprなしでコーディングした場合と同じくらい効率的ですか?constexpr例えば:

constexpr int combinations(int n, int k)
{
//Assume all error conditions and edge conditions are taken care with ternary operator ?:
return fact(n)/(fact(k)*fact(n-k);
}

関数が実行時に記述されている場合は、メモ化の恩恵を受けることができますconstexprそれでも可能だったとはいえ、実行時にも可能な限り効率的かつ効率的に機能を表現することは難しいと思います。

4

1 に答える 1

3

いいえ、私が知る限り、特定の呼び出しでコンパイラが関数をどのように使用しているかを検出したり、constness に応じて異なる実装を使用するようにコンパイラに指示したりすることはできません。

しかし、まず第一に、constexpr関数は 1 つのreturnステートメントに制限されます。つまり、コンパイラは (ほとんどの場合) 末尾再帰の最適化を簡単に適用して、再帰呼び出しをループに変えることができます。したがって、この質問は早期最適化を行う方法に関するものであり、これは良い考えではありません。低レベルの最適化はコンパイラの仕事です。

第二に、本当にコンパイラの仕事をしたいのであれば、2 つの異なる関数の実装を無意味に 1 つの実装に詰め込もうとするのではなく、関数に名前を付けることができます。それはどのような目的に役立ちますか?あいまいさだけ。


与えられた特定の例については、

constexpr int fact(int n)
{
    return (n)?n*fact(n-1):1;
}

コンパイラは、末尾再帰として書き直すことができることを認識しなければなりません。それについての以前の SO の質問のテストから思い出したように、Visual C++ コンパイラでさえそれを行います。いくつかの説明のつかない理由 (おそらく元の x86 プロセッサの設計に関係している) により、浮動小数点型の使用によって困惑しました: 同じ高レベルのロジック、異なる低レベルの結果。

この関数がアプリを容認できないほど遅くするものであることを測定して発見した後、マシンコードを検査し、コンパイラが関数の末尾を認識できないことを発見した後、少し劇的ではないコンパイラのヘルプの作業努力として-再帰性があるため、次のように書き換えることができます。

constexpr int fact( int multiplier, int n )
{
    return (n != 0? fact( multiplier*n, n-1 ) : multiplier);
}

constexpr int fact( int n )
{
    return fact( 1, n );
}

免責事項: コンパイラの汚れた手が触れていないコード。

于 2013-01-18T11:07:26.697 に答える