基礎
變長參數模板的一般格式是:
// 編譯遞歸的終止位置
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