2016-07-08 12 views
2

私は多くの質問と回答を読んだことがありますが、this questionが私の目を最も惹きました。それとその答えは役に立ちますが、私はまだ非型のテンプレート引数を使うことの背後にある使用法と理論を完全には理解していないと感じています。それらは、いつ使われるかについての多くの有用な例を提供していますが、実際には非型テンプレート引数の背後にある理論については何の示唆もしていません。非型テンプレート引数が使用されるのはなぜですか?

私は、具体例ではなく、より一般的には、通常の関数引数だけではなく、非型テンプレート引数を使用する傾向にあることに興味があります。

私はコンパイル時に決定され、新しい呼び出しごとに、型のないテンプレート引数の値が決定された新しい関数が作成されることを理解します。だから、なぜ私は関数に必要なパラメータを入力して、同じ結果を持っていると思いますか?おそらく、コンパイルされた関数が1つしかない場合、同じ関数の多くの異なるインスタンスを作成したいのです。

最後に、this pagecplusplus.comの最後のセクションに記載されているように、#2ではなく#1を行う傾向がありますか?

#1:

template <class T, int N> 
T fixed_multiply (T val) 
{ 
    return val * N; 
} 

int main() { 
    std::cout << fixed_multiply<int,2>(10) << '\n'; 
    std::cout << fixed_multiply<int,3>(10) << '\n'; 
} 

は#2:

template <class T> 
T fixed_multiply (T val, int N) 
{ 
    return val * N; 
} 

int main() { 
    std::cout << fixed_multiply<int>(10, 2) << '\n'; 
    std::cout << fixed_multiply<int>(10, 3) << '\n'; 
} 

また、いずれかの任意のパフォーマンス上の利点や、そのようながあるでしょうか?非タイプのテンプレート引数を使用することで利益を得ることができる一般的なアプリケーションはありますか?これは特定のアプリケーションで特定の結果を得るために使用される非常に技術的なパラメータですか?

編集:これは何らかの理由で重複しているとマークされていますが、最初の段落で私の同様の質問の理由が説明されています。

+0

'std :: transform'を使用してvectorのすべての要素に5を掛けるために第2の変種を使用してください –

+0

あなたが表示する例ではそれほど大きな違いはありませんが、大きな違いは、テンプレート化されたバージョンをコンパイル時に評価される 'constexpr'関数にすることができるからです。非型テンプレート引数は、['std :: array'](http://en.cppreference.com/w/cpp/container/array)や実際の配列を得るためにCスタイルの配列を取る関数sizeをタイプし、サイズを実行時引数として渡す必要はありません。 –

+2

@Amitそれは質問の最初の行にリンクされた同じ質問です。 – Barmar

答えて

4

これらは、コンパイラーが適切なコードを保持したままコンパイル時の最適化を行う(または必要とする)必要がある場合に便利です。私は簡単にいくつかの例をリストアップします:

支店除去

void doSomething(bool flag) { 
    if (flag) { 
    //whatever 
    } 
} 

template <bool flag> 
void doSomething() { 
if (flag) { 
    //whatever 
} 

}上記の例では

、あなたがdoSomethingを呼び出すときは、常にコンパイル時にflagの価値を知っていれば、 doSomethingTruedoSomethingFalse関数を手作業で作成することなく、多くのコードを繰り返す必要がない場合でも、ブランチのコストを回避できます。このトリックは、たとえばネットワークコードでsendとrecvの間のコードを因数分解したい場合に便利です。また、コードはパフォーマンス重視のスタックに深く入り込みます。それが重要なループ内で立ち往生している場合は、上記の例では

避け動的メモリ管理

void someFunction(size_t size, float* inout) { 
    std::vector<float> tmp(size); 
    //do something with inout and tmp and then store result in inout 
} 

template <size_t N> 
void someFunction(float *inout) { 
    float tmp[N]; //or std::array if you like 
    //do something with inout and tmp and store result in inout 
} 

は、第二のバージョンは、より良い実行されます。

特別な最適化

がループを考えてみましょう許可:

for (size_t i = 0; i < N; ++i) { 
    //some arithmetic 
} 

コンパイラはNが4または8であることを知っているならば、同等のSIMD命令を使用して算術演算を置き換えることができ、またはでありNがランタイム引数であった場合よりも、ループをよりインテリジェントに展開してください。この種のことは、GPUプログラミング(CUDA)の世界では一般的です。

テンプレートメタプログラミングあなたはテンプレートといくつかの非自明なコード生成を行うと、時々、あなたはコンパイル時に物事を反復しなければならない

。これを行うには、テンプレート型以外の引数が必要です。引数パックとstd::tupleはよくあるケースですが、ここにはさらに多くの例があります。

動的な(ランタイム)引数を取るコードは常に柔軟性があるので、何かをテンプレート引数にする必要があります。パフォーマンスを大幅に向上させて正当化することができます。一方、一連の列挙体に重複したコードを書くことがある場合は、おそらくテンプレートを使って試してみるべきです。

関連する問題