变长参数模板的通常格式是:ios
// 编译递归的终止位置 template <T arg> void foo(T arg) { // do something with arg } // 展开编译表达式 template <typename T, typename ...Args> void foo(T arg, Args... args) { foo(arg, args...); }
上述的代码中,须要有一个特化的函数,转么做为编译器递归终止的出口。在C++17以及以上的版本中,能够使用constexpr
结合sizeof...
关键字,实现模板编译器的递归。web
给出代码示例,注意都是编译器展开的代码模板,而非运行期。svg
#include <iostream> #include <string> template <typename T, typename ...Args> void Print(T&& arg, Args&&... args) { std::cout << arg << std::endl; if constexpr (sizeof...(args) > 0) { std::cout << "remaining args = " << sizeof...(args) << std::endl; Print(args...); } } int main() { Print(1, std::string("hello"), 3.14, 'c'); return 0; }
上述代码中,sizeof...
计算的是剩余参数的数量,而if constexpr
表示的编译器的条件判断。函数
注意一个点,上述的Print在函数体内部,必须是constexpr
表达式内部,不然没法识别。由于constexpr
是实际编译期间才会执行推导的部分spa
变长模板是能够执行编译期计算的,好比为参数添加上对应的值,代码示例:code
#include <iostream> #include <string> template<typename T, typename ...Args> void Print(T &&arg, Args &&... args) { std::cout << arg << std::endl; if constexpr (sizeof...(args) > 0) { Print(args...); } } template<typename... Args> void PrintDouble(Args &&... args) { Print(args + args...); // 对应位置的参数相加 } template<typename... Args> void PrintAdd1(Args &&... args) { Print(args + 1 ...); // 每一个参数加1,不适合string } int main() { PrintDouble(1, std::string("hello"), 3.14); PrintAdd1(1, 3.14); return 0; }
对于索引来讲,一样适用,给个代码示例xml
#include <iostream> #include <vector> template<typename T, typename... Args> void Print(T &&arg, Args &&... args) { std::cout << arg << std::endl; if constexpr (sizeof...(args) > 0) { Print(args...); } } template<size_t... Idx, typename Container> void PrintIdx(Container &&c) { Print(c[Idx]...); } int main() { std::vector<int> vec{0, 1, 2, 3, 4, 5}; PrintIdx<0, 4, 2 ,3, 1>(vec); return 0; }