- 可變參數
- C風格的可變參數
- C風格可變參數的使用
- C++11可變參數模板
- 遞歸展開參數包
- 參數列表展開
- 折疊表達式
- STL中的emplace插入接口
可變參數
C風格的可變參數
可變參數是一種語言特性,可以在函數聲明中使用省略號...
來表示函數接受可變數量的參數。
例如典型的printf()
函數:
int printf (const char * szFormat, ...);// printf("%d + %d = %d \n", 1, 2, 1+2);
第一個參數是格式化字符串,后面的...
是可變參數
//使用宏定義與可變參數
//__VA_ARGS__表示宏定義中的可變參數部分
#define LOG(format, ...) printf(format, ##__VA_ARGS__)
如果可變參數列表為空,##
會刪除前面的逗號,避免語法錯誤
C風格可變參數的使用
在函數體內,可以使用 va_list 類型的變量和va_start, va_arg, va_end,函數來處理可變參數。
在 <stdarg.h> 頭文件中包含
paramN : 函數定義中最后一個命名參數的名稱。隨后調用va_arg提取的參數是后面的參數
示例:
void my_printf(const char* format,...)
{//定義一個va_list類型變量va_list argList; //初始化argList,獲取format后面的額外參數va_start(argList, format); //依次獲取可變的參數值。需要指定參數值的數據類型while (*format != '\0') {if(*format == '%') {format++; switch (*format) {case 'd': //整數 cout << va_arg(argList, int) << " ";break;case 'f': //浮點數 cout << va_arg(argList, double) << " ";break;}}format++;}//釋放va_list變量va_end(argList);
}my_printf("%d, %f", 25, 3.22);
C++11可變參數模板
C++11新增特性。可變參數模板是一種用于處理具有不定數量參數的函數模板的特性。
定義:
template<class ...Args>
viod func(Args... args);
Args
為參數包名,前加可變參數...
進行聲明。參數包中可以包含0~n個不同類型參數
通過使用參數包和展開表達式,才能獲取并使用參數
遞歸展開參數包
示例:
//當遞歸展開介紹,調用該終止函數
int sum() {return 0;
}// 遞歸情況:將第一個參數與后面的參數相加
template<typename T, typename... Args>
T sum(T first, Args... rest) {return first + sum(rest...);
}std::cout << sum(1, 2, 3, 4, 5) << std::endl; // 輸出:15
std::cout << sum(10.5, 20.5, 30.5) << std::endl; // 輸出:61.5
使用參數包時,需要加上展開操作符
...
遞歸展開時,需要注意,如果沒有終止函數,當參數包中的參數個數為0后,會發生死循環
上述代碼的終止函數也可以寫一下形式:
//當沒有參數時,返回0
template<typename T>
T sum(T value) {return value;
}
參數列表展開
通過參數列表(initializer_list)獲取參數包中的參數
如果參數包中的各個參數都是相同類型,這可以通過列表初始化的形式,將其轉移到數組中。
template<typename... Args>
void func(Args... args) { int dummy[] = { args...};dummy[0];//使用...
}
該操作,不支持0個參數的參數包(不能分配常量大小為0的數組)
對于不同類型的參數,可以利用參數列表結合逗號表達式,將參數包展開為逗號分隔的一系列獨立的參數。
template<class T>
void doSomething(const T& t)
{...//使用
}template<typename... Args>
void myFunction(Args... args) {// 使用展開操作符將參數包展開int arr[] = { (doSomething(args), 0)...};// ...
}
通過初始化列表來初始化一個變長數組,然后再對數組arr
初始化時,會執行expand函數中的逗號表達式(從左到右計算表達式并將最后一個表達式的值返回)。最終會在創建完一個int arr[sizof ...args] = {0}
的數組同時,處理參數包中的參數。
當然這里的int
型以及逗號表達式中的0
值,都沒有后續的使用意義。
myFunction(1, 2.5, "hello");//參數包展開為:
int arr[] = {(doSomething(1), 0),(doSomething(2.5), 0),(doSomething("hello"), 0)
};
如果不想在0個參數時
myFunction();
,編譯報錯,可增加無參的重載
//可增加一個無參的函數重載,來對0個參數特殊處理
void doSomething();
折疊表達式
折疊表達式(Fold Expressions) 是 C++17 引入的一種簡化可變參數模板(variadic templates)的語法特性。
有四種:
(pack op ...) // 一元左折疊
(... op pack) // 一元右折疊
(init op ... op pack) // 二元左折疊
(pack op ... op init) // 二元右折疊
使用:
template<typename... Args>
auto sum(Args... args) {return (args + ...); // 一元左折疊
}sum(1,2,3,4); // (((1 + 2) + 3) + 4)
template<typename... Args>
auto sum(Args... args) {return (... + args); // 一元右折疊
}sum(1,2,3,4); // (1 + (2 + (3 + 4)))
template<typename... Args>
auto sum_with_init(int init, Args... args) {return (init + ... + args); // 二元左折疊
}sum_with_init(0,1,2,3); // (((0 + 1) + 2) + 3)
template<typename... Args>
auto sum_with_init(int init, Args... args) {return (args + ... + init); // 二元右折疊
}sum_with_init(0,1,2,3); // (1 + (2 + (3 + 10)))
template<typename... Args>
void myFunction(Args... args) {(doSomething(args), ...); // 折疊表達式
}myFunction(1, 2.5, "hello");
// (doSomething(1), (doSomething(2.5), (doSomething("hello"))));
- pack :
args
- op :
,
STL中的emplace插入接口
例如在vector
容器中:
該成員函數,就是使用了可變參數模板,使用上和原來的插入接口push_back
差別不大
vector<std::pair<int, string>> v;
v.push_back(make_pair( 0, "hello" ));
v.emplace_back(1, "world");
make_pair會多構建一次string對象的消耗
🦀🦀觀看~~