【C++可变模板参数】

张开发
2026/4/10 2:27:09 15 分钟阅读

分享文章

【C++可变模板参数】
C11 可变模板参数总结搞懂参数包、包扩展和 emplace1. 为什么 C11 需要可变模板参数在 C11 之前如果我们想写一个“参数个数可变、参数类型也可变”的函数基本只能靠写很多重载或者用...C 风格可变参数类型不安全而可变模板参数Variadic Templates解决的是参数个数可变参数类型可变编译期类型安全能和泛型、右值引用、完美转发组合一句话它是现代 C 泛型编程的基础设施。2. 基础语法模板参数包 函数参数包2.1 模板参数包templateclass...ArgsArgs代表“0 个或多个类型”。2.2 函数参数包templateclass...ArgsvoidFunc(Args...args){}args代表“0 个或多个函数参数”。如果要支持完美转发常见写法是templateclass...ArgsvoidFunc(Args...args){}3.sizeof...统计参数包个数#includeiostreamusingnamespacestd;templateclass...ArgsvoidPrintCount(Args...args){coutsizeof...(args)endl;// 参数个数//sizeof...(Args) 也可统计}intmain(){doublex2.2;PrintCount();// 0PrintCount(1);// 1PrintCount(1,string(xxxxx));// 2PrintCount(1.1,string(xxxxx),x);// 3}4. 包扩展最核心参数包不能像数组那样args[i]访问能做的核心操作就是“展开expand”。4.1 递归展开C11 经典写法#includeiostream#includestringusingnamespacestd;// 递归终止voidShowList(){coutendl;}// 每次取一个剩下的继续递归templateclassT,class...ArgsvoidShowList(T x,Args...args){coutx ;ShowList(args...);}templateclass...ArgsvoidPrint(Args...args){ShowList(args...);}intmain(){Print();Print(1);Print(1,string(hello));Print(1,string(hello),2.2);}这段代码的本质编译器会在编译期“展开”为多组具体函数调用。4.2 模式扩展Func(args...)不是唯一玩法templateclassTconstTGetArg(constTx){coutx ;returnx;}templateclass...ArgsvoidArguments(Args...args){}templateclass...ArgsvoidPrint(Args...args){Arguments(GetArg(args)...);// 包扩展模式对每个 args 套一层 GetArg}关键理解GetArg(args)...等价于GetArg(arg1), GetArg(arg2), GetArg(arg3)...5. 可变模板参数 完美转发高频考点5.1 为什么要std::forwardArgs(args)...因为有名字的变量都是左值表达式。如果你只写args...右值信息会丢失可能触发错误重载或性能下降。templateclass...ArgsvoidWrapper(Args...args){Target(std::forwardArgs(args)...);// 保留原始值类别}5.2 一句话区分std::move(x)无脑转右值std::forwardT(x)按模板推导结果“有条件转发”6. 真实价值场景emplace系列接口emplace_back/emplace本质是可变模板参数 完美转发。templateclass...Argsvoidemplace_back(Args...args);templateclass...Argsiteratoremplace(const_iterator pos,Args...args);相比push_backpush_back往往是“先构造对象再拷贝/移动进容器”emplace_back可以“直接把构造参数传到底层在容器内存上原地构造”示例理解std::vectorstd::pairstd::string,intv;v.emplace_back(apple,1);// 直接构造 pairstring,int这就是“参数包一路转发到节点/元素构造函数”的价值。7. 可变模板参数常见坑7.1 不能把参数包当数组用错误思路// args[i] // 不支持参数包不是运行时容器而是编译期语法结构。7.2 忘记递归终止函数递归展开必须有“空包终止版本”不然会无限模板实例化报错。7.3 忘记完美转发导致右值退化Args... args 下层调用foo(args...)会把右值当左值传下去。7.4const对象即使move也不一定能移动const T通常无法真正“偷资源”很多时候仍走拷贝语义。

更多文章