介绍
模板的参数在照往常来说是固定的大小之后让编译器进行推导,但是现在c++11出现了新的东西就是模板函数的参数可以使不确定的。下面来看一个实例
template <typename T , typename... Types>
void Print(const T & FirstArg,const Types &... arg)
{
cout << firstAge <<endl;
Print(args...);
}
上面所有加上...的部分不是省略,而是语法的要求,所以这个位置都是语法的规定。我们输入的参数可以是任意的。将变量前后分开,分为第一个模板参数,和后面的一堆参数。然后会循环调用自己,将后面的包的内容传进Print函数里面。
需要注意的点
其实后面的这些变脸就是一个所谓的pack,用于模板参数就是模板参数包,用在函数类型里面就是函数类型包。
当最后一个元素没有的时候,还需要设计一个函数,最后一次调用这个函数结束递归调用。如果没有处理最后一种没有参数的情况的函数的话,编译器就会报错。
如果我们想知道传进来的参数个数
调用sizeof...(args),这个...在sizeof的后面
一个令人震惊的点
看下面的这个函数
template<typename... Types>
void print(const Types&... args)
{ /*...*/}
这个函数我们会认为它和上面的那个函数是有重复的,编译应该是不过的,但是并没有其实是可以并存的。至于说每次传进来的数据怎么去使用,这个的话还需要实验去确定,但是我个人估计,编译器在编译的时候会查看每个模板的一个特化的情况,有的时候模板都是打包的,但是有一些变量是特化出来的,所以更加符合要求,编译器就会优先使用这些函数。
Tuple的例子
首先看一下Tuple的代码里面应用到这个可变模板参数的地方
template <typename... Values> class tuple;
template<> class tuple<>{};
//这里面可以看到的是tuple是继承于后面的包的类型的
//这一点应用起来就很牛逼
template<typename Head,tyepname... tail>
class tuple<Head,Tail...>:private tuple<Tail...>
{
//我都不知道这都能进行typedef
typedef tuple<Tail...> inherited;
public:
tuple(){}
typle(Head v,Tail... tail)
:m_head(v),inherited(tail...){}
//head赋给了我们的成员变量 m_head,然后这个继承关系就是递归的继承
//最后我们需要写出一个没有元素初始化的tuple在最上面,来退出递归。
typename Head::type head() {return m_head;}
inherited & tail() {return *this}
private:
Head m_head;
简单解释一下上面的例子
也就是说你个一个tuple变量几个参数传进去之后,假如我们使用的下面这个例子的变量
tuple<int,string,float> eg(1,"hello world",1.02);
这个例子里面,首先就是类型的一个继承关系 int (m_head(1)) ->string(m_head("hello world"))->float(m_head(1.02)),也就是说tuple<float>类型是所有的其他类型的父类,然后float再去继承那个空的tuple。
这个例子就已经很方便的解释了可变模板参数的作用,但是有几个注意点
1.需要添加递归的结束条件(变量,函数等等),不然编译会报错
2.需要注意函数的特化和泛化程度,这样才能正确调用到合适的参数。