1. 程式人生 > >C++17使用std::apply和fold expression對tuple進行遍歷

C++17使用std::apply和fold expression對tuple進行遍歷

C++17使用std::apply和fold expression對std::tuple進行遍歷

std::apply函式

先來看這個std::apply函式,這個函式定義在tuple標頭檔案中,函式簽名如下:

template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t);

該函式接受兩個引數,第一個是一個函式物件,第二個是一個Tuple物件

來看一個最簡單的示例:

#include <tuple>
#include <iostream>
int main() { // 兩個元素相加 std::cout << std::apply([](auto x, auto y) { return x + y; }, std::make_tuple(1, 2.0)) << '\n'; }

輸出結果是3

這個例子中第一個引數使用Lambda匿名函式將tuple中的兩個元素相加,第二個使用std::make_tuple函式構造一個只含有兩個元素的tuple

fold expression

這個特性是C++ 17中我覺得很有用的一個新特性,使用規則有下面四條:

  1. Unary right fold (EE opop …) becomes (E1E_1 opop (… opop (EN1E_{N-1} opop ENE_N)))

  2. Unary left fold (… opop EE) becomes (((E1E_1 opop E2E_2) opop …) opop ENE_N)

  3. Binary right fold (EE opopopop II) becomes (E1E_1 opop (… opop (EN1E_{N−1}

    1 opop (ENE_N opop II))))

  4. Binary left fold (II opopopop EE) becomes ((((II opop E1E_1) opop E2E_2) opop …) opop ENE_N)

這裡的EE指的是Expression(符合C++語法的表示式),opop指的是operator(操作符),NN是parameter pack(可變引數)的個數,II是一個常數。

可能看這個規則有些抽象,我們來看一些具體的例子:

#include <tuple>
#include <iostream>

int main() {
    // 多個元素相加,使用parameter pack
    std::cout << std::apply([](auto&& ... args) { return (args + ...); },
                            std::make_tuple(1, 2.f, 3.0)) << '\n';
    // 遍歷tuple並輸出,注意逗號操作符的使用
    std::apply([](auto&&... args) { ((std::cout << args << '\n'), ...); },
                std::make_tuple(1, 2.f, 3.0));
}

輸出如下:

6
1
2
3

第6行中,std::apply函式的第一個引數是一個Lambda匿名函式,函式的引數是一個可變引數args,函式體中只有一條語句args + ...,這個情況就是上面的第一種情況:這裡的EE就是argsopop就是+,所以展開來就是args1+args2+args3args_1 + args_2 + args_3(因為引數的個數是3)。

第9行中,Lambda匿名函式的函式體是((std::cout << args << '\n'), ...)這是一個逗號操作符,也屬於上面四種情況中的第一種:這裡的EE就是std::cout << args << '\n')opop就是,,所以這一行就列印輸出了tuple的每一個元素。如果在C++17之前想要遍歷tuple就比較麻煩,需要很多額外的操作。

參考資料