C++ 如何遍历 std::tuple 的元素?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1198260/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can you iterate over the elements of an std::tuple?
提问by emsr
How can I iterate over a tuple (using C++11)? I tried the following:
如何迭代元组(使用 C++11)?我尝试了以下方法:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
but this doesn't work:
但这不起作用:
Error 1: sorry, unimplemented: cannot expand ‘Listener ...' into a fixed-length argument list.
Error 2: i cannot appear in a constant expression.
错误 1:抱歉,未实现:无法将 'Listener ...' 扩展为固定长度的参数列表。
错误 2:i 不能出现在常量表达式中。
So, how do I correctly iterate over the elements of a tuple?
那么,如何正确迭代元组的元素?
采纳答案by éric Malenfant
Boost.Fusionis a possibility:
Boost.Fusion是一种可能性:
Untested example:
未经测试的示例:
struct DoSomething
{
template<typename T>
void operator()(T& t) const
{
t.do_sth();
}
};
tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());
回答by emsr
I have an answer based on Iterating over a Tuple:
我有一个基于迭代元组的答案:
#include <tuple>
#include <utility>
#include <iostream>
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
print(std::tuple<Tp...>& t)
{ }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
print(std::tuple<Tp...>& t)
{
std::cout << std::get<I>(t) << std::endl;
print<I + 1, Tp...>(t);
}
int
main()
{
typedef std::tuple<int, float, double> T;
T t = std::make_tuple(2, 3.14159F, 2345.678);
print(t);
}
The usual idea is to use compile time recursion. In fact, this idea is used to make a printf that is type safe as noted in the original tuple papers.
通常的想法是使用编译时递归。事实上,这个想法被用来制作一个类型安全的 printf,如原始元组论文中所述。
This can be easily generalized into a for_each
for tuples:
这可以很容易地推广为一个for_each
for 元组:
#include <tuple>
#include <utility>
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
{ }
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT f)
{
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
Though this then requires some effort to have FuncT
represent something with the appropriate overloads for every type the tuple might contain. This works best if you know all the tuple elements will share a common base class or something similar.
尽管这需要一些努力来FuncT
为元组可能包含的每种类型表示具有适当重载的内容。如果您知道所有元组元素将共享一个共同的基类或类似的东西,这将最有效。
回答by xskxzr
In C++17, you can use std::apply
with fold expression:
在 C++17 中,您可以使用std::apply
with fold 表达式:
std::apply([](auto&&... args) {((/* args.dosomething() */), ...);}, the_tuple);
A complete example for printing a tuple:
打印元组的完整示例:
#include <tuple>
#include <iostream>
int main()
{
std::tuple t{42, 'a', 4.2}; // Another C++17 feature: class template argument deduction
std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}
This solution solves the issue of evaluation order in M. Alaggan's answer.
该解决方案解决了M. Alaggan 的回答中的求值顺序问题。
回答by Mohammad Alaggan
In C++17 you can do this:
在 C++17 中,你可以这样做:
std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);
This already works in Clang++ 3.9, using std::experimental::apply.
这已经适用于 Clang++ 3.9,使用 std::experimental::apply。
回答by pepper_chico
Use Boost.Hana and generic lambdas:
使用 Boost.Hana 和通用 lambdas:
#include <tuple>
#include <iostream>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>
struct Foo1 {
int foo() const { return 42; }
};
struct Foo2 {
int bar = 0;
int foo() { bar = 24; return bar; }
};
int main() {
using namespace std;
using boost::hana::for_each;
Foo1 foo1;
Foo2 foo2;
for_each(tie(foo1, foo2), [](auto &foo) {
cout << foo.foo() << endl;
});
cout << "foo2.bar after mutation: " << foo2.bar << endl;
}
回答by DanielS
C++ is introducing expansion statementsfor this purpose. They were originally on track for C++20 but narrowly missed the cut due to a lack of time for language wording review (see hereand here).
为此,C++ 引入了扩展语句。他们最初是在 C++20 的轨道上,但由于缺乏语言措辞的时间,差点错过了晋级(见这里和这里)。
The currently agreed syntax (see the links above) is:
当前商定的语法(见上面的链接)是:
{
auto tup = std::make_tuple(0, 'a', 3.14);
template for (auto elem : tup)
std::cout << elem << std::endl;
}
回答by Stypox
A more simple, intuitive and compiler-friendly way of doing this in C++17, using if constexpr
:
一种在 C++17 中执行此操作的更简单、直观和编译器友好的方法,使用if constexpr
:
// prints every element of a tuple
template<size_t I = 0, typename... Tp>
void print(std::tuple<Tp...>& t) {
std::cout << std::get<I>(t) << " ";
// do things
if constexpr(I+1 != sizeof...(Tp))
print<I+1>(t);
}
This is compile-time recursion, similar to the one presented by @emsr. But this doesn't use SFINAE so (I think) it is more compiler-friendly.
这是编译时递归,类似于@emsr 提供的递归。但这不使用 SFINAE 所以(我认为)它对编译器更友好。
回答by user1447257
First define some index helpers:
首先定义一些索引助手:
template <size_t ...I>
struct index_sequence {};
template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};
template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};
With your function you would like to apply on each tuple element:
使用您的函数,您希望应用于每个元组元素:
template <typename T>
/* ... */ foo(T t) { /* ... */ }
you can write:
你可以写:
template<typename ...T, size_t ...I>
/* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
std::tie(foo(std::get<I>(ts)) ...);
}
template <typename ...T>
/* ... */ do_foo(std::tuple<T...> &ts) {
return do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}
Or if foo
returns void
, use
或者如果foo
返回void
,请使用
std::tie((foo(std::get<I>(ts)), 1) ... );
Note: On C++14 make_index_sequence
is already defined (http://en.cppreference.com/w/cpp/utility/integer_sequence).
注意:在 C++14make_index_sequence
上已经定义(http://en.cppreference.com/w/cpp/utility/integer_sequence)。
If you do need a left-to-right evaluation order, consider something like this:
如果您确实需要从左到右的评估顺序,请考虑以下内容:
template <typename T, typename ...R>
void do_foo_iter(T t, R ...r) {
foo(t);
do_foo(r...);
}
void do_foo_iter() {}
template<typename ...T, size_t ...I>
void do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
do_foo_iter(std::get<I>(ts) ...);
}
template <typename ...T>
void do_foo(std::tuple<T...> &ts) {
do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}
回答by Marc Mutz - mmutz
You need to use template metaprogramming, here shown with Boost.Tuple:
你需要使用模板元编程,这里用 Boost.Tuple 显示:
#include <boost/tuple/tuple.hpp>
#include <iostream>
template <typename T_Tuple, size_t size>
struct print_tuple_helper {
static std::ostream & print( std::ostream & s, const T_Tuple & t ) {
return print_tuple_helper<T_Tuple,size-1>::print( s, t ) << boost::get<size-1>( t );
}
};
template <typename T_Tuple>
struct print_tuple_helper<T_Tuple,0> {
static std::ostream & print( std::ostream & s, const T_Tuple & ) {
return s;
}
};
template <typename T_Tuple>
std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) {
return print_tuple_helper<T_Tuple,boost::tuples::length<T_Tuple>::value>::print( s, t );
}
int main() {
const boost::tuple<int,char,float,char,double> t( 0, ' ', 2.5f, '\n', 3.1416 );
print_tuple( std::cout, t );
return 0;
}
In C++0x, you can write print_tuple()
as a variadic template function instead.
在 C++0x 中,您可以print_tuple()
改为编写可变参数模板函数。
回答by Marcin Zawiejski
Here's an easy C++17 way of iterating over tuple items with just standard library:
这是仅使用标准库迭代元组项的简单 C++17 方法:
#include <tuple> // std::tuple
#include <functional> // std::invoke
template <
size_t Index = 0, // start iteration at 0 index
typename TTuple, // the tuple type
size_t Size =
std::tuple_size_v<
std::remove_reference_t<TTuple>>, // tuple size
typename TCallable, // the callable to bo invoked for each tuple item
typename... TArgs // other arguments to be passed to the callable
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
if constexpr (Index < Size)
{
std::invoke(callable, args..., std::get<Index>(tuple));
if constexpr (Index + 1 < Size)
for_each<Index + 1>(
std::forward<TTuple>(tuple),
std::forward<TCallable>(callable),
std::forward<TArgs>(args)...);
}
}
Example:
例子:
#include <iostream>
int main()
{
std::tuple<int, char> items{1, 'a'};
for_each(items, [](const auto& item) {
std::cout << item << "\n";
});
}
Output:
输出:
1
a
This can be extended to conditionally break the loop in case the callable returns a value (but still work with callables that do not return a bool assignable value, e.g. void):
这可以扩展为在可调用对象返回值的情况下有条件地中断循环(但仍然适用于不返回 bool 可分配值的可调用对象,例如 void):
#include <tuple> // std::tuple
#include <functional> // std::invoke
template <
size_t Index = 0, // start iteration at 0 index
typename TTuple, // the tuple type
size_t Size =
std::tuple_size_v<
std::remove_reference_t<TTuple>>, // tuple size
typename TCallable, // the callable to bo invoked for each tuple item
typename... TArgs // other arguments to be passed to the callable
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
if constexpr (Index < Size)
{
if constexpr (std::is_assignable_v<bool&, std::invoke_result_t<TCallable&&, TArgs&&..., decltype(std::get<Index>(tuple))>>)
{
if (!std::invoke(callable, args..., std::get<Index>(tuple)))
return;
}
else
{
std::invoke(callable, args..., std::get<Index>(tuple));
}
if constexpr (Index + 1 < Size)
for_each<Index + 1>(
std::forward<TTuple>(tuple),
std::forward<TCallable>(callable),
std::forward<TArgs>(args)...);
}
}
Example:
例子:
#include <iostream>
int main()
{
std::tuple<int, char> items{ 1, 'a' };
for_each(items, [](const auto& item) {
std::cout << item << "\n";
});
std::cout << "---\n";
for_each(items, [](const auto& item) {
std::cout << item << "\n";
return false;
});
}
Output:
输出:
1
a
---
1