まず、テンプレート宣言の型名(ここでは T
)の前に pack
演算子を加えます。次に、関数パラメータの型名(ここでは args
)の前にも pack
演算子を加えます。以下が基本的な記述法です。
template<typename ... T> void foo(T ... args);
連続したピリオド三文字が pack
演算子です。他の演算子と同じく、前後に空白文字を入れることができます。
ここで、関数へ渡される型名 T
とパラメータ args
には、内部に複数の型とパラメータが pack
されています。これらは、使用する前に unpack
演算子を使って分解する必要があります。分解には、再帰呼び出しを使います。
void foo() { } template<typename T, typename ... Args> void foo(const T & arg, Args ... args) { std::cout << arg << std::endl; foo(args...); }
foo
の再帰呼び出しに使用しているパラメータ名の直後に記述しているピリオド三文字が unpack
演算子です。この演算子をつけたパラメータを含んだ関数呼び出しは、pack
されている複数の型を展開した結果の順列に合致したパラメータ群を持つオーバーロードされた関数を選択します。
前の例では、foo(1, "2");
のように呼び出した場合、最初に foo(int, ...)
、次に foo(char *, ...)
、最後に foo()
と合致することになります。pack
演算子は、ゼロ個のパラメータも保持するので、前の例の場合は終端としての foo()
が必要になってきます。この関数がないと、「foo()
が見つからない」といったコンパイルエラーになります。
pack
されたパラメータの個数は、sizeof...
演算子で取得できます。
auto counts = sizeof...(args);