本文主要介紹如何展開可變參數的參數包
1.C語言的va_list展開可變參數
#include <iostream>
#include <cstdarg>void printNumbers(int count, ...) {// 聲明va_list類型的變量va_list args;// 使用va_start將可變參數寫入變量argsva_start(args, count);for (int i = 0; i < count; i++) {// 調用va_arg依次獲取可變參數的參數值int value = va_arg(args, int);std::cout << value << " ";}// 使用va_end清理變量argsva_end(args);std::cout << std::endl;
}int main() {printNumbers(3, 1, 2, 3); // 輸出: 1 2 3printNumbers(5, 1, 2, 3, 4, 5); // 輸出: 1 2 3 4 5
}
? ? ? ?使用va_list感覺還挺麻煩的,需要提前知道參數個數,并且還得需要知道入參類型。因此不如直接使用數組了。
2.C++的initializer_list展開可變參數
#include <iostream>template <typename T>
void printNumbers(std::initializer_list<T> nums) {// 獲取可變參數長度為std::cout << "可變參數長度為:" << nums.size() << std::endl;std::cout << std::endl;// 遍歷輸出可變參數,方法1for (const T& i: nums) {std::cout << i << " ";}std::cout << std::endl;
}int main() {printNumbers({"1","qq"});printNumbers({1, 2, 3});
}
3.可變參數模板--遞歸函數方式展開可變參數
#include <iostream>using namespace std;void print() {cout << endl;
}template <typename T>
void print(const T& t) {cout << t << endl;
}template <typename First, typename... Rest>
void print(const First& first, const Rest&... rest) {cout << first << ", ";print(rest...); // recursive call using pack expansion syntax
}int main()
{print(); // calls first overload, outputting only a newlineprint(1); // calls second overload// these call the third overload, the variadic template,// which uses recursion as needed.print(10, 20);print(100, 200, 300);print("first", 2, "third", 3.14159);
}
? ? ? ?使用這種方法,可變參數可以是任意類型的,不需要都統一是一個類型了。
? ? ? ?這種方法需要提供一個參數包展開的函數和一個遞歸終止函數,二者同名。遞歸終止函數的參數可以為0,1,2或者多個(一般用到0個或1個),當參數包中剩余的參數個數等于遞歸終止函數的參數個數時,就調用遞歸終止函數,則函數終止。
4.可變參數模板--折疊表達式展開可變參數
#include <iostream>template<typename... Args>
void prints(Args... args) {//注意args在前,省略號在后否則報錯//std::cout <<sizeof...(args)<<std::endl;//sizeof...() 運算符可獲取參數包參數個數((std::cout << args << " "), ...); // 折疊表達式展開參數包std::cout << std::endl;
}int main() {prints("test", 2, 'k'); // 輸出: test 2 kprints(1, 2, 3, 4, 5); // 輸出: 1 2 3 4 5
}
? ? ? ?折疊表達式是C++17 才引入的新特性,使用折疊表達式可以很方便實現展開可變參數,但是可讀性也會大打折扣。在此使用的是折疊表達式的逗號運算符。折疊表達式還會分為左折疊和右折疊。
5.可變參數模板--結合std::tuple展開可變參數
#include <iostream>
#include <tuple>// 終止條件:當索引等于元組大小時
template<size_t I = 0, typename Tuple>
typename std::enable_if<I == std::tuple_size<Tuple>::value, void>::type
printtp(const Tuple&) {// 空實現,遞歸終止
}// 遞歸展開:當索引小于元組大小時
template<size_t I = 0, typename Tuple>
typename std::enable_if<I < std::tuple_size<Tuple>::value, void>::type
printtp(const Tuple& t) {std::cout << std::get<I>(t) << std::endl; // 打印當前元素printtp<I + 1>(t); // 遞歸調用下一個索引
}// 包裝函數:將參數包轉換為元組并調用printtp
template<typename... Args>
void print(Args... args) {printtp(std::make_tuple(args...));
}int main() {print(1, "hello", 3.14); // 輸出:1\nhello\n3.14return 0;
}
? ? ? ?在這使用了std::tuple數據結構以及類型萃取中常用的std::enable_if。其實本質上還是遞歸模板參數展開,用起來可能不如直接使用上述方法三。
本文參考:
https://zhuanlan.zhihu.com/p/684295744
C++17常用新特性(十一)---折疊表達式-騰訊云開發者社區-騰訊云
省略號和可變參數模板 | Microsoft Learn
https://zhuanlan.zhihu.com/p/731232067
C++中神奇的tuple:詳解使用技巧和實例解析-騰訊云開發者社區-騰訊云
C++之std::enable_if_std enable if-CSDN博客
c++11——可變參數模板 - 農民伯伯-Coding - 博客園