Variadic Template Parameterの型引数のことを、テンプレートパラメーターパック(Template Parameter Pack)という。
template < typename ... Types >
struct Foo { }
ここでは、Typesは、テンプレートパラメーターパックである。
テンプレートパラメーターパックを、関数の引数に取る場合、これを、パラメーターパックという。
template < typename ... Types >
void f( Types ... args ) { }
ここでは、argsは、パラメーターパックである。
クラステンプレートの場合、テンプレートパラメーターパックは、必ず、テンプレートパラメーターリストの最後に書かなければならない。
// well-formed
template < typename T, typename ... Types >
struct well_formed ;
// ill-formed
template < typename ... Types, typename T >
struct ill_formed ;
これは、テンプレート実引数を指定しても、曖昧になるからである。
関数テンプレートのテンプレートパラメーターリストの場合には、そのような制限はない。なぜならば、関数テンプレートは、引数をdeduceできるからである。ただし、パラメーターパックは、関数のパラメーターリストの最後でなければならない。
// well-formed.
template < typename ... Types, typename T >
void well_formed( T, Types ... ) ;
// ill-formed.
// パラメーターパック、Typesは、パラメーターリストの最後にしか書けない。
template < typename ... Types, typename T >
void ill_formed( Types ..., T ) ;
これができれば、実に、メタプログラミング的な意味で、便利なのだが、残念ながら、これはできない。
ちなみに、こういう事はできる。これは、パラメータリストの型ではなく、パラメーターの型の一部だからである。
template < typename ... Types1, typename ... Types2 >
void f( void (*p1)( Types1 ...), void (*p2)( Types2 ... ) )
{
// それぞれ、引数の型のデフォルトの値で呼び出す。
p1( Types1()... ) ;
p2( Types2()... ) ;
}
void g( int, int, int ) { }
void h( float, float ) { }
int main()
{
f( &g, &h ) ;
}
// 呼び出すためだけの関数。
template < typename ... Types >
void call( Types ... ) { }
// 任意の戻り値を持つ関数ポインタを、任意個引数にとる。
template < typename ... Types >
void f( Types (* ... t)( void ) )
{
// 関数の実引数の評価順序は、定義されていない。
// どの順番で、引数の関数が呼び出されるかは、実装依存である。
// ただし、すべての関数は、呼び出される。
call( t()... ) ;
}
int g( void ) { std::cout << "g" << std::endl ; return 0 ; }
int h( void ) { std::cout << "h" << std::endl; return 0 ; }
int main()
{
f( &g, &h, &g, &h, &g, &h ) ;
}
このcall()関数はネタであって、普通の場合は、以下のような関数を書いた方が良い。
template < typename T >
void call( T t )
{
t() ;
}
template < typename T, typename ... Types >
void call( T t, Types ... args )
{
t() ;
call( args ... ) ;
}
template < typename ... Types >
void f( Types (* ... t)( void ) )
{
call( t... ) ;
}
これを組み合わせると、
template < typename ... ReturnTypes, typename ... ParameterTypes >
void f( ReturnTypes (* ... t)( ParameterTypes... ) ) { }
int g( char, short, int, long, float, double ) { return 0 ; }
int main()
{
f( &g, &g, &g, &g ) ;
}
もちろん、これは、ジェネリックではない。しかし、この記述は、引数にとるすべての関数ポインタが、同じシグネチャであることを保証できる。普通のプログラマは、こう書けば良い。
template < typename ... Functors >
void f( Functors ... functors ) { }