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 ) { }
No comments:
Post a Comment