C++遍历std::tuple(C++14 ~ C++20)
本文展示了遍历std::tuple
的方式:
首先比较容易想到的是利用C++14的std::make_index_sequence
与std::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;
}
输出结果: