2016-06-21 16 views
0

テンプレート関数fooを、異なる(型のない)パラメータのシーケンスで呼び出すときに、下限と上限の間で連続する整数値を使用したいとします。例えば:複数のパラメータ値を持つテンプレート関数を呼び出す

template <int K> void foo(){ ... } 

int const UPPER = 10, LOWER = 5; 

for(int i = LOWER; i <= UPPER; i++) 
    foo<i>(); 

これ、もちろん、iはコンパイル時に知られていないため、動作しません。私は次の1つの実行からUPPERLOWERを変更するには予定のため、これは特にあり

foo<5>(); foo<6>(); foo<7>(); foo<8>(); foo<9>(); foo<10>(); 

:私は次のように記述することなく、プログラムのこの種を達成するための方法を探しています。配列の要素が一定であるものの、iが知られていない、再び

int const arr[6] = {5, 6, 7, 8, 9, 10}; 

for(int i = LOWER; i <= UPPER; i++) 
    foo<arr[i]>(); 

しかし、:

私の唯一のアイデアは、テンプレートパラメータに送信される整数の定数配列を作成することでしたコンパイル時にはどちらもarr[i]です。助言がありますか?

ありがとうございます。

+2

それはあなたが[ 'STDを使用したい場合があり次のようになります。 :integer_sequence'](http://en.cppreference.com/w/cpp/utility/integer_sequence) – NathanOliver

+0

foo()を '構造体foowrapper'テンプレートに置くと、もっと簡単になります。 ? – lorro

答えて

3

LowerUpperに等しいかどうかに応じて、2つのテンプレートとstd::enable_ifを使用して1つを選択できます。 と等しい場合は、何もしません。そうでない場合は、foo<Lower>()を呼び出し、パラメータLower + 1Upperで再帰します。このテンプレートを考える

template <int Lower, int Upper> 
typename std::enable_if<Lower == Upper, void>::type callFoo() 
{ 

} 

template <int Lower, int Upper> 
typename std::enable_if<Lower != Upper, void>::type callFoo() 
{ 
    static_assert(Lower < Upper, "Lower must be less than or equal to Upper"); 

    foo<Lower>(); 
    callFoo<Lower + 1, Upper>(); 
} 

、次の行は、K5ためfoo<K>()678910を呼び出します。

callFoo<5, 11>(); 
+0

非常に洗練されたソリューション。ありがとうございました。 – tmnol

+0

このコードは、C++の以前のバージョン(C++ 11より前)と互換性がある可能性のある回避策を知っていますか? – tmnol

0

私が知る限り、テンプレートはコンパイル時に実際の構造に解決されるので、intを関数の引数として渡す必要があります。

+1

OPはコンパイル時に整数を知っていることです。コンパイル時のループに相当するものをテンプレート引数として使用することができます。 – chris

+0

私は知っていますが、私はこのようなものが存在するかどうかはわかりませんでした。 @ NathanOliverは、私が気づいていなかったものを指していました。std :: integer_sequence – MaciekGrynda

+0

@MaciekGrynda整数を関数の引数として渡せば、実際にはもっと簡単になります。しかし、私は質問の範囲を超えている理由で、できません。 – tmnol

3

あなたは0昇順から数字のコンパイル時のリストを取得するためにstd::integer_sequenceを利用して、その後、追加することができ、あなたのオフセット:

// Here we take the lower bound and the sequence 0 to (Upper - Lower). 
// We call foo with each number in the sequence added to the lower bound. 
template<int Lower, int... Ints> 
void call_foo_with_range_helper(std::integer_sequence<int, Ints...>) { 
    // A common trick to expand the parameter pack without recursion or fold expressions 
    (void)std::initializer_list<int>{(foo<Lower + Ints>(), 0)...}; 
} 

// This simply makes it easier for the caller to use. 
// We take the lower and upper bounds only. 
template<int Lower, int Upper> 
void call_foo_with_range() { 
    call_foo_with_range_helper<Lower>(std::make_integer_sequence<int, Upper - Lower + 1>()); 
} 

int main() { 
    int const UPPER = 10, LOWER = 5; 

    call_foo_with_range<LOWER, UPPER>(); 
} 
+0

ありがとうございました。@TerraPassの答えは、私が理解するのが楽になってから最善のものをマークしました(私のコードを読んでいる人もそうだと思いますが)どちらも良い解決策です。 – tmnol

+2

@ TudorManole、問題ありません。 'initializer_list'ビジネス全体は残念であり、fold式が利用可能になると、'(foo ()、...); 'で置き換えることができます。それ以外に、 'std :: integer_sequence'はあなたが本当にそれを感謝する前に数回必要とするものの一つです。この場合、作業の大部分は範囲をサポートしており、0から始まる範囲はサポートしていません。 – chris

関連する問題