一、可变参函数模板
// 可变参函数模板 template <typename... T> // 表示0到多个不同的类型 void MyFun(T... args) // 表示0到多个不同类型的参数(可以同类型,也可以不同类型) { cout << sizeof...(T) << endl; cout << sizeof...(args) << endl; // 都是表示可变参的个数 } // 可变参函数模板,参数包的展开 template <typename T, typename... U> // void MyFun2(const T& first, const U&... args) // 一个参数,后面跟一包参数,这种形式是个参数包展开 { cout << "收到的参数为:" << first << endl; MyFun2(args...); // 递归调用,不断获取第一个参数,注意此处参数的写法 args... } void MyFun2() // 解析参数包里面的参数一定要有个无参的终止函数,否则编译报错 { cout << "执行递归终止函数" << endl; }
二、可变参类模板
1、通过递归继承方式展开参数包
// 可变参类模板 // 通过递归继承的方式展开参数包 template <typename... T> class MyClass {}; // 可变参类模板的声明 template <> class MyClass<> // 该类为 0 参数的特化版本,可写可不写 { // 有了这个类,下面的可变参模板类会最先构造该类 public: MyClass() { cout << "0参数的特化版本" << endl; } }; template <typename T, typename... U> class MyClass<T, U...> : private MyClass<U...> // 偏特化 { public: MyClass() : m_val(0) { cout << "构造函数 this =" << this << endl; } MyClass(T first, U... args) : m_val(first), MyClass<U...>(args...) { //带参构造函数 cout << "m_val = " << m_val << endl; } T m_val; }; void MyClassFun() { MyClass<int, double, float> myClass(1, 2.2, 2.3); // }
执行结构,根据执行结果,可以推断出类的继承顺序:先构造父类,在构造子类
2、通过递归组合方式展开参数包
与继承方式展开很类似,可以对比 1 和 2 中修改的地方
// 通过递归组合的方式展开参数包 template <typename... T> class MyClass {}; // 可变参类模板的声明 template <> class MyClass<> // 该类为 0 参数的特化版本,可写可不写 { // 有了这个类,下面的可变参模板类会最先构造该类 public: MyClass() { cout << "0参数的特化版本" << endl; } }; template <typename T, typename... U> class MyClass<T, U...> // : private MyClass<U...> { public: MyClass() : m_val(0) { cout << "构造函数 this =" << this << endl; } MyClass(T first, U... args) : m_val(first), m_other(args...) //MyClass<U...>(args...) { //带参构造函数 cout << "m_val = " << m_val << endl; } T m_val; MyClass<U...> m_other; // 组合关系 }; void MyClassFun() { MyClass<int, double, float> myClass(1, 2.2, 2.3); }
执行的结果与1中相同,可以推断,组合对象先构造
3、通过tuple和递归展开参数包
这种展开方式需要些类的特化版本
实现思路:计数器从0开始,每处理一个参数,计数器+1,一直到吧所有参数处理完,最后整一个模板偏特化,作为递归调用结束
// 通过tuple和递归展开参数包 // iCount 用于统计第几个参数,maxCount表示一共有多少个参数 template<int iCount, int maxCount, typename... T> class MyClass { public: static void StaticFun(const tuple<T...>& t) { cout << "value = " << get<iCount>(t) << endl; MyClass<iCount + 1, maxCount, T...>::StaticFun(t); // 递归调用 } }; // 需要一个特化版本,用于结束递归调用 template<int maxCount, typename... T> class MyClass<maxCount, maxCount, T...> { public: static void StaticFun(const tuple<T...>& t) { cout << "特化版本" << endl; } }; // 通过一个模板函数来调用tuple参数类型 template <typename... T> void MyTemplateFun(const tuple<T...>& t) // 可变参函数模板 { MyClass<0, sizeof...(T), T...>::StaticFun(t); } void MyClassFun() { tuple<int, double, float> myTuple(1, 2.2, 2.3); // tuple 元组,里面可以是任意类型参数的组合 //cout << get<0>(myTuple) << endl; // 取元组中的第一个元素 MyTemplateFun(myTuple); }
执行结果如下,可以推断出函数调用的顺序
小结:参数包的展开一般都是采用递归的方法
三、模板模板参数
即 可以作为模板的模板参数
// 模板 模板参数:本身是一个模板参数,这个模板参数同时又是模板 //template <typename T,template<class> class U > template < typename T, // 模板参数 template<class> class U // 模板模板参数,一般作用是作为一个容器 // template<typename W> typename U // 这与上一种写法意思是一样的,但稍有复杂 // 其中的 W 并没有任何用处,只是占位 > class MyClass { public: MyClass() { for (int i = 0; i < 10; ++i) { m_Uval.push_back(i); // 该构造函数是否能编译过,取决于传入的容器是否支持push_back操作 } } public: T m_val; U<T> m_Uval; // U相当于一个类模板 // 所以如果想通过 U<T> 的方式来使用U,就必须将U当作一个模板模板参数 }; template<typename T> using MyVector = vector<T, allocator<T>>; // 常用写法,来定义模板别名 int main() { //MyClass<int, vector> myVector; // 直接传vector 编译会报错,因为分配器不知道如何分配 MyClass<int, MyVector> myVector; system("pause"); return 0; }
原文:https://www.cnblogs.com/zhiminzeng/p/13173993.html