C++11引入了variadic template(可變參數模板)的概念,這是一個可以接受任意參數個數的模板(爲了實現任意參數個數,必須使用到遞歸).
維基百科上面有着比較詳細的講解:link
有幾個重要的地方:
- 想要接受0個或更多個的參數
template<typename First, typename... Rest>
- 想要接受1個或以上的參數
template<typename... values>
- 省略號(…) 在參數左邊,聲明瞭一個參數包(parameter pack)
- 省略號(…) 在參數右邊,代表參數包展開(parameter pack expansion),將參數寶解開爲一組實參
來看一段代碼:
#include <cstring>
#include <iostream>
#include <sstream>
// We define a namespace so printf does not clash
namespace practice {
template<typename T>
size_t replace(const char * format, T value) {
for (size_t i = 0; i < strlen(format); i++) {
if (format[i] == '$') {
std::cout << value;
return i + 1;
}
else {
std::cout << format[i];
}
}
return strlen(format);
}
// base function
void printf(const char * format) { std::cout << format; }
// 'recursive' function
template<typename First, typename... Rest args>
void printf(const char * format, First value, Rest args... args) {
// each time we skip one $ and repeat the same operation at the next position
printf(&format[replace(format, value)], args...);
}
} // namespace practice
int main(void) {
// Should print: Hello World foobar 4
practice::printf("$ $ foo$ $\n", "Hello", "World", "bar", 4);
return EXIT_SUCCESS;
}
這裏我們利用了variadic template實現了一個printf,與內置的printf不同的是,我們使用$作爲格式控制符。
利用variadic template的整體思路如下:
- 定義一個“基礎函數”(base function),定義了只有一個參數的時候應該進行的操作
- 定義一個“遞歸函數”,逐個處理變量
- 在這裏,遞歸函數中,我們一次替換一個控制符$,最後在基礎函數裏面,已經替換了所有的控制符,直接輸出就好
C++14又進一步的引入了type deduction概念,使得variadic template更加強大,下面來看看如何將variadic template和lambda搭配使用
用一段代碼來分析:
#include <iostream>
// TODO: Base case
template<typename Ftype>
Ftype compose(const Ftype & fn) {
Ftype f = fn;
return f;
}
// TODO: Recursive case
template<typename Ftype, typename... Other>
auto compose(Ftype fn, Other... other) {
return [fn, other...](const int x) { return compose(other...)(fn(x)); };
}
int main(void) {
auto add_func = [](const int x) { return x + 5; };
auto sub_func = [](const int x) { return x - 3; };
auto mul_func = [](const int x) { return x * 1; };
int res = compose(add_func, sub_func, mul_func)(0);
std::cout << "Result: " << res << "\n";
}
這裏主函數中我們聲明瞭三個lambda函數,分別完成加減乘的運算,然後利用variadic template將其合併成一個lambda函數。
- 仔細觀察主函數我們可以知道,compose函數的返回值應該是一個lambda函數(因爲直接使用了調用函數的方式傳遞了參數0)故compose的返回值應爲
[](const int x){ return ...}
的形式 - 再分析可知,compose函數傳入的全是lambda函數,基本邏輯應該是,先利用第一個lambda函數和參數x計算出新的x值,再將這個值當做參數傳遞給下一層的lambda函數
- 當只剩下一個參數時(基礎函數),由於剩下的本身就是一個lambda函數,所以直接返回即可
- 最終目標應該是生成如下的嵌套lambda函數
auto func = [add_func, sub_func, mul_func](const int x) {
return [sub_func, mul_func](const int x) {
return [mul_func](const int x){
return mul_func(x);
}(sub_func(x));
}(add_func(x));
};