Variadic Templates 的引入,消去了烦冗的模板特化。
一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <iostream> double do_sum() { return 0; } template < typename T, typename ... Args > double do_sum( T&& t, Args&& ... args ) { return t + do_sum( args... ); } int main() { std::cout << do_sum( 1.0, 2.0, 3.0, 4.0 ) << std::endl; return 0; } |
这里需要注意的有两点:
- double do_sum() 这个函数必须在变长模板函数 double do_sum( T&& t, Args&& … args ) 之前声明
- 变长模板函数实现中必须使用另外一个函数递归实现
另外要看到,在变长模板函数声明中使用 … 的方法
- 模板上用的是 template< typename… Args>
- 函数参数中用的是 double do_sum(Arg … arg)
- 函数体中用的是 do_sum(arg…)
大致可以看出,有 typename 的时候 .. 跟 typename 后边,否则跟在 Arg 后边,最后则是在参数 arg 后边
如果需要知道到底传入了多少个参数可以这样来
1 2 3 4 5 | template < typename ... Args> std:: size_t how_many_args(Args ... args) { return sizeof ...(args); } |
variadic template 基本使用到这里就差不多了,下边的内容略略而过即可
再次注意这个 …,来个稍微有点复杂的
1 2 3 4 5 6 7 8 | template < typename ... T> void f(T (* ...t)( int , int )); int add( int , int ); float subtract( int , int ); void g() { f(add, subtract); } |
再来一个多继承
1 2 3 4 5 6 7 | template < typename ... Mixins> class X : public Mixins... { public : X( const Mixins&... mixins) : Mixins(mixins)... { } }; |
整数也是可以放在 variadic template 上的
1 2 3 4 | template < class ... Types> class Tuple; // Types is a template type parameter pack template < class T, int ... Dims> struct multi array; // Dims is a non-type template parameter pack |
缺省template参数也是可以的,比如
1 2 3 4 5 6 | template < class T = char > class String; String<>* p; // OK: String<char> String* q; // syntax error template < typename ... Elements> class Tuple; Tuple<>* t; // OK: Elements is empty Tuple* u; // syntax error |
还有 template template 的这种
1 2 3 4 5 6 7 8 9 10 11 | template < class T> class A { /* ... */ }; template < class T, class U = T> class B { /* ... */ }; template < class ... Types> class C { /* ... */ }; template < template < class > class P> class X { /* ... */ }; template < template < class ...> class Q> class Y { /* ... */ }; X<A> xa; // okay X<B> xb; // ill-formed: default arguments for the parameters of a template template argument are ignored X<C> xc; // ill-formed: a template parameter pack does not match a template parameter Y<A> ya; // ill-formed: a template parameter pack does not match a template parameter Y<B> yb; // ill-formed: a template parameter pack does not match a template parameter Y<C> yc; // okay |
也特别注意 Y<B> 和 X<B> 无论哪个都不能通过。这是因为在 template 中,模板类型顺序是不可以搞错的,比如:
1 2 3 4 5 6 7 8 9 | template < class T1, class T2> struct A { void f1(); void f2(); }; template < class T2, class T1> void A<T2,T1>::f1() { } // OK template < class T2, class T1> void A<T1,T2>::f2() { } // erro |
于是同样就有
1 2 3 4 5 6 | template < class ... Types> struct B { void f3(); void f4(); }; template < class ... Types> void B<Types...>::f3() { } // OK |
更详细的有
1 2 3 4 5 6 7 8 9 10 11 12 | template < class X, class Y> X f(Y); template < class X, class Y, class ... Z> X g(Y); int i = f< int >(5.6); // Y is deduced to be double int j = f(5.6); // ill-formed: X cannot be deduced f< void >(f< int , bool >); // Y for outer f deduced to be // int (*)(bool) f< void >(f< int >); // ill-formed: f<int> does not denote a // single function template specialization int k = g< int >(5.6); // Y is deduced to be double, Z is deduced to an empty sequence f< void >(g< int , bool >); // Y for outer f deduced to be // int (*)(bool), Z is deduced to an empty sequence |
注意这种特化
1 2 3 4 5 6 7 8 9 10 11 12 | template < typename ...> struct Tuple { }; template < typename ... Types> void g(Tuple<Types...>); // #1 template < typename T1, typename ... Types> void g(Tuple<T1, Types...>); // #2 template < typename T1, typename ... Types> void g(Tuple<T1, Types&...>); // #3 g(Tuple<>()); // calls #1 g(Tuple< int , float >()); // calls #2 g(Tuple< int , float &>()); // calls #3 g(Tuple< int >()); // calls #3 |
还有多个 variadic template 嵌套着用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | template < typename ...> struct Tuple {}; template < typename T1, typename T2> struct Pair {}; template < typename ... Args1> struct zip { template < typename ... Args2> struct with { typedef Tuple<Pair<Args1, Args2>...> type; }; }; typedef zip< short , int >::with<unsigned short , unsigned>::type T1; // T1 is Tuple<Pair<short, unsigned short>, Pair<int, unsigned> > typedef zip< short >::with<unsigned short , unsigned>::type T2; // error: different number of arguments specified // for Args1 and Args2 template < typename ... Args> void g(Args... args) { f( const cast< const Args*>(&args)...); // okay: ‘‘Args’’ and ‘‘args’’ are expanded f(5 ...); // error: pattern does not contain any parameter packs f(args); // error: parameter pack ”args” is not expanded f(h(args...) + args...); // okay: first ‘‘args’’ expanded within h, second ‘‘args’’ expanded within f. } |
再举一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | template < typename T> void print_comma_separated_list(T&& value) { std::cout<<value<<std::endl; } template < typename First, typename ... Rest> void print_comma_separated_list(First&& first,Rest&& ... rest) { std::cout<<first<< "," ; print_comma_separated_list(rest...); } int main() { print_comma_separated_list(42, "hello" ,2.3, 'a' ); print_comma_separated_list( "hello" ,2.3, 'a' ); print_comma_separated_list(2.3, 'a' ); print_comma_separated_list( 'a' ); return 0; } |
[task]
特别说明:
本文例程多摘自 c++0x N2242,这里可以找到:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdf
[/task]