3

コンパイル時に定数を計算するための C++ でのメタプログラミング手法をいくつか知っています。ほとんどの場合、メタ関数での分岐は、標準の if/else とは対照的に、コンパイル時に評価できる三項演算子を介して行われます。

しかし、この種の機能に関して:

template <unsigned int N>
void f()
{
    if (N == 0) {
        // Some computations here
    } else if (N <= 42) {
        // Some computations here
    } else {
        // Some computations here
    }
}

コンパイラは何をしますか(仮定-O3)?コンパイラf<0>()は、 が常に最初のケースでf<32>()分岐し、常に 2 番目のケースでf<64>()分岐し、常に 3 番目のケースで分岐することを認識しています。

コンパイラは、常に になるブランチを削除しますfalseか? 唯一の有効なケースに直接分岐しますか?

4

2 に答える 2

6

オプティマイザは未使用のブランチ内のブランチとコードを削除しますが、注意してください: オプティマイザがコードを見る機会を得る前に、コンパイラは関数を処理する必要があります。つまり、すべてのブランチがすべてに対して有効 (コンパイル可能) でなければなりません。の値N

たとえば、2 番目のブランチに次のものが含まれているとします。

} else if (N <= 42) {
   char data[50 - N];
// other code

N >= 50分岐がオプティマイザによって削除されても、コンパイラはテンプレートのインスタンス化に失敗します。

于 2013-07-09T18:31:22.200 に答える
2

生成されたアセンブリを表示するオンライン コンパイラであるhttp://gcc.godbolt.org/に次のように入力しました。代わりに、アセンブリを出力するためにサポートされている任意のスイッチを使用して、独自のコンパイラを使用できます。

volatile int i;

template <unsigned int N>
void f()
{
    if (N == 0) {
        i = 1;
    } else if (N <= 42) {
        i = 2;
    } else {
        i = 3;
    }
}

template void f<0>();
template void f<10>();
template void f<100>();

これが私が得たアセンブリです

void f<0u>():                           # @void f<0u>()
    movl    $1, i
    ret

void f<10u>():                          # @void f<10u>()
    movl    $2, i
    ret

void f<100u>():                         # @void f<100u>()
    movl    $3, i
    ret

i:
    .long   0                       # 0x0

ご覧のとおり、各インスタンス化でデッド コードがすべて削除されています。

実際、このコードは最適化を無効にして生成されました。私が使用したコンパイラ (clang) は、そもそもデッド コードの命令を生成しません。他のコンパイラは異なる動作をする場合があります。独自のコンパイラの動作を自分でテストする必要があります。

于 2013-07-09T18:55:51.193 に答える