Loading... > 英文:Variadic Templates ,C++11 允许模板定义中包含 0 ,到多个(任意个)模板参数。 # 一、可变参函数模板 ## 1. 基本含义 可变参函数模板中,传递进来的一包实参怎样展开:**一般都采用递归函数的方式展开参数包**。 要展开,要求,在可变参函数模板代码中,`有一个参数包展开函数,以及 一个 同名的递归终止函数`。 ```cpp #include <iostream> using namespace std; //可变参函数模板 template <typename... T> //...代表参数包 ...后面再加T void myvtfunct(T... args) //T:一包类型,args:一包形参 ,T称为“可变参类型”,T中包含的是**0到多个不同的类型(一包类型)** //args称为一包/一堆 参数(函数模板的形参:0个到多个),每个参数的类型可以各不相同。 { cout << "-----------begin-----------" << endl; cout << sizeof...(args) << endl; //收到的参数数量,sizeof...固定语法,C++11中引入,用于表示收到的模板参数个数或者类型数量。 //sizeof...针对的只能是这种...的可变参,后面圆括号中可以是函数模板的形参args,也可以是类型模板参数T. cout << sizeof...(T) << endl; //收到的类型数量 cout << "-----------end-----------" << endl; } //再实现一个同名的递归终止函数(这是个真正的函数),位置放在 参数包展开函数 的前面 void myvtfunct() //这是个普通函数,而不是函数模板 { cout << "参数包展开时执行了递归终止函数myvtfunct()" << endl; } int main() { myvtfunct(); myvtfunct(10,20); myvtfunct(10, 25.8,"abc",68); //10和25.8指定为double类型 myvtfunct<double,double>(10, 25.8, "abc", 68, 73); //指定部分类型,让编译器去推断另一部分类型,是允许的。 } 输出结果: 参数包展开时执行了递归终止函数myvtfunct() -----------begin----------- 2 2 -----------end------------- -----------begin----------- 4 4 -----------end----------- -----------begin----------- 5 5 -----------end----------- ``` T... 代表可变参类型 args称为一包、一堆参数,**每个参数的类型可以各不相同** sizeof...(args) C++11中引入 收到模板参数的数量 **上面这个测试范例实际上无法将参数包进行展开!!** ## 2、将参数展开 ```cpp #include <iostream> using namespace std; //再实现一个同名的递归终止函数 要放到参数包展开的上面,放下面会编译报错,提示没找到myvtfunct1的重载函数 void myvtfunct1() //这是个普通函数,而不是函数模板 { cout << "参数包展开时执行了递归终止函数myvtfunct1()" << endl; } //先实现参数包展开函数 template <typename T,typename...U>//拆成一个和其余的 void myvtfunct1(T firstarg, U...otherargs) //(10, "abc", 12.7); { cout << "收到的参数值:" << firstarg << endl; myvtfunct1(otherargs...); //递归调用,注意塞进来的是一包形参,这里的...不能省略 } int main() { myvtfunct1(10, "abc", 12.7); return 0; } 输出结果: 收到的参数值:10 收到的参数值:abc 收到的参数值:12.7 参数包展开时执行了递归终止函数myvtfunct1() ``` void myvtfunct1() 必须放void myvtfunct1(T firstarg, U...otherargs) 之前 实际每次调用myvtfunct 的效果如下: a)myvtfunct(10, "abc", 12.7); b)myvtfunct( "abc", 12.7); c)myvtfunct(12.7); d)myvtfunct(); **类模板的泛化版本,typename ... Args2必须写在最后面(且只有一个),下面两种情况都会导致编译出错** 情况1: template <typename ...Args1,typename ... Args2> class myclasst2 {}; 情况2: template <typename ...Args, typename U> class myclasst3 {}; ## 3、 编译期间if语句(constexpr if) 针对上面代码,可以利用**C++17**进行代码优化 ```cpp //先实现参数包展开函数 template <typename T,typename...U> void myvtfunct2(T firstarg, U...otherargs) //(10, "abc", 12.7); { cout << "收到的参数值:" << firstarg << endl; //myvtfunct(otherargs...); //递归调用,注意塞进来的是一包形参,这里的...不能省略 if constexpr (sizeof...(otherargs) > 0) //constexpr必须有否则无法成功编译,圆括号中是常量表达式。 { myvtfunct2(otherargs...); //递归调用,注意塞进来的是一包形参,这里的...不能省略 } else { //不需要做什么,其实整个else都可以省略不写。 } } int main() { myvtfunct2(10, "abc", 12.7);//g++ 2_4.cpp -std=c++17 return 0; } 输出结果: 收到的参数值:10 收到的参数值:abc 收到的参数值:12.7 ``` **使用if constexpr的话就不用写递归终止函数** 可以使用dumpbin工具查看.o文件 生成的会是三个myvtfunct2形参数量不一样的函数 ### 深入认识if constexpr a)不满足条件的分支,也同样会被编译器编译(被编译器进行语法检查)。 b)if constexpr所指定的条件必须是常量:理解成普通if语句,只是判断条件从执行期间挪到了编译期间 总结:if constexpr的存在,完善了模板与泛型编程中的程序执行路径选择问题。 # 二、可变参重载 一般来说,调用普通函数和调用函数模板都合适的时候,编译器优先选择普通函数。 ```cpp template<typename... T> void myfunc3(T... arg) { cout << "myfunc3(T... arg)执行了!" << endl; } template<typename... T> void myfunc3(T*... arg) { cout << "myfunc3(T*... arg)执行了!" << endl; } void myfunc3(int arg) { cout << "myfunc3(int arg)执行了!" << endl; } int main() { myfunc3(NULL); // void myfunc(int arg) myfunc3(nullptr); //nullptr是空指针 void myfunc(T... arg) myfunc3((int *)nullptr);//void myfunc(T*... arg) return 0; } 输出结果: myfunc3(int arg)执行了! myfunc3(T... arg)执行了! myfunc3(T*... arg)执行了! ``` # 三、递归形式展开参数包 递归组合或者递归继承方式展开参数包 最后修改:2025 年 07 月 01 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏