C++遍历std::tuple(C++14 ~ C++20)

本文展示了遍历std::tuple的方式:

首先比较容易想到的是利用C++14的std::make_index_sequencestd::get结合取值,然后配合std::initializer_list进行包展开:

// since C++14
class Func0 {
    template<typename T, typename F, size_t... I>
    void init(T&& t, F&& f, std::index_sequence<I...>) {
        std::initializer_list<int>{ (f(std::get<I>(t)), 0)... };
    }
public:
    template<typename T, typename F>
    auto operator()(T&& t, F&& f) {
        init(t, f, std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<T>>>{});
    }
};

到了C++17,我们有了折叠表达式(Fold expressions),就可以直白一点了:

// since C++17
class Func1 {
    template<typename T, typename F, size_t... I>
    void init(T&& t, F&& f, std::index_sequence<I...>) {
        ((f(std::get<I>(t))), ...);
    }
public:
    template<typename T, typename F>
    auto operator()(T&& t, F&& f) {
        init(t, f, std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<T>>>{});
    }
};

C++20允许lambda添加模板参数,因此我们可以进一步限制这个遍历器的作用域:

    // since C++20
    auto Func2 = []<typename T, typename F>(T && t, F && f) {
        [&] <size_t ...I>(std::index_sequence<I...>) {
            ((f(std::get<I>(t))), ...);
        }(std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<T>>>{});
    };

然后我们还可以利用C++17提供给我们的std::apply取值:

// since C++17
class Func3 {
public:
    template<typename T, typename F>
    auto operator()(T&& t, F&& f) {
        std::apply(
            [&f](auto&&... args) {
                ((f(args)), ...);
            }, t
        );
    }
};

或者干脆把这个std::apply给拿出来,这种应该算是最简单的:

// since C++17
std::apply(
    [&PrintV](auto&&... args) {
        ((PrintV(args)), ...);
    }, t
);

完整测试程序:

#include <iostream>
#include <tuple>
#include <type_traits>
#include <initializer_list>


// since C++14
class Func0 {
    template<typename T, typename F, size_t... I>
    void init(T&& t, F&& f, std::index_sequence<I...>) {
        std::initializer_list<int>{ (f(std::get<I>(t)), 0)... };
    }
public:
    template<typename T, typename F>
    auto operator()(T&& t, F&& f) {
        init(t, f, std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<T>>>{});
    }
};

// since C++17
class Func1 {
    template<typename T, typename F, size_t... I>
    void init(T&& t, F&& f, std::index_sequence<I...>) {
        ((f(std::get<I>(t))), ...);
    }
public:
    template<typename T, typename F>
    auto operator()(T&& t, F&& f) {
        init(t, f, std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<T>>>{});
    }
};

// since C++17
class Func3 {
public:
    template<typename T, typename F>
    auto operator()(T&& t, F&& f) {
        std::apply(
            [&f](auto&&... args) {
                ((f(args)), ...);
            }, t
        );
    }
};


int main(int argc, char* argv[])
{
    // 测试一下
    // auto t = std::make_tuple(1, 2.f, 3., '4', "5");  // for C++14
    std::tuple t(1, 2.f, 3., '4', "5"); 
    auto PrintV = [](auto&& v) { std::cout << v << ' '; };

    Func0()(t, PrintV);
    std::cout << "\n\n";
    
    Func1()(t, PrintV);
    std::cout << "\n\n";
    
    // since C++20
    auto Func2 = []<typename T, typename F>(T && t, F && f) {
        [&] <size_t ...I>(std::index_sequence<I...>) {
            ((f(std::get<I>(t))), ...);
        }(std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<T>>>{});
    };
    Func2(t, PrintV);
    std::cout << "\n\n";
    
    Func3()(t, PrintV);
    std::cout << "\n\n";

    // since C++17
    std::apply(
        [&PrintV](auto&&... args) {
            ((PrintV(args)), ...);
        }, t
    );
    std::cout << "\n\n";
    return 0;
}

输出结果:
在这里插入图片描述