基础
变长参数模板的一般格式是:
// 编译递归的终止位置
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...
关键字,实现模板编译器的递归。
给出代码示例,注意都是编译器展开的代码模板,而非运行期。
#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
表示的编译器的条件判断
拓展
变长模板是可以执行编译期计算的,比如为参数添加上对应的值,代码示例:
#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;
}
对于索引来说,同样适用,给个代码示例
#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;
}
参考
- https://zh.cppreference.com/w/cpp/language/fold
- https://cloud.tencent.com/developer/article/1347876