variadic template with lambda

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));
    };
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章