在C++11之前, 有两个典型的受制于模板功能不强而导致代码重复难看的问题, 那就 function object 和 tuple。 拿 function objects 来说, 需要一个返回类型参数及N个参数类型参数。 但因为变长参数模板不受支持,导致不得不重复书写7、8个模板类,但最终也只能支持7、8个参数的 function object。C++11中最终为我们带来了强大的变长 参数模板功能,这些问题也随之迎刃而解了。
可变参数模板(Variadic Template)故名思义,即可以接受任意数量参数的类/函数模板。 其声明方式为
template<typename... Args> class VariadicTemplate;
当然non-type参数也是支持的比如 template<int... Args> class VariadicIntTemplate;
这里Args代表0-N个类型参数,也就是说VariadicTemplate<>也是有效的模板实例。
C++11 翻新了省略号(...)操作符来支持对这种模板参数的访问及使用。
类型展开
将所有模板参数展开在当前位置,用来调用其它模板、声明函数指针类型
template<typename ...Args> int foo() { int (*p)(Args...) = bar<Args...>; p(1, 0.5); return bar<Args...>(1, 0.5); }
对于 foo<int, float> 实例,其 p 的类型为 int (*)(int, float) 而 return 语句将调用 bar<int, float>(1, 0.5)
此外还可以展开类型参数用来指定基类列表
template<typename... Args> class foo : Args...{ }; foo<bar, barz> f; // 该类继承 bar, barz 两个基类
Parameter Packs / Initialization Lists
通过省略号操作符可以声明参数包,这些参数包可以在函数的参数位置展开。
template<typename ...Args> void foo(Args... args) { }
当使用 foo<int, float> 时,其有两参数,一个整数,一个float,因此调用方法为 foo<int, float>(1, 0.1f);
此外还可以将参数包展开到构造函数的初始化列表
template<typename... Args> class foo : Args...{ public: foo(Args...args):Args(args)...{ } }; foo<bar, barz> f(bar(), barz());
sizeof... operator
要获取模板变长参数的实参长度,使用 sizeof... 运算符,它也可以被用于 Parameter Packs
template<typename ... Args> size_t foo() { return sizeof...(Args); } template<typename ... Args> size_t fooz(Args ...args) { return sizeof...(args); }
以上就是变长参数的基本用法,但真正发挥作用,往往还要借助模板特化 (Template Specialization)
可变参数模板与模板特化
可变参数模板与普通模板一样,可以进行特化,从而实现一些有意义的模式
template<typename ... Args> void output(Args ... args) { } template<typename Arg0, typename ... Args> void output(Arg0 arg0, Args ... args) { std::cout << arg0; output<Args...>(args...); } template<> void output() { } /*------------------------------*/ typedef decltype(std::cout) cout_type; auto endl = std::endl<cout_type::char_type, cout_type::traits_type>; output(1, 0.3, (void*)nullptr, "string", ‘c‘, endl);
以上代码输出
10.30stringc