遍历元组

我如何迭代一个元组(使用C ++ 11)? 我尝试了以下,但是这不起作用:

for(int i=0; i<std::tuple_size<T...>::value; ++i) std::get<i>(my_tuple).do_sth(); 

错误1:抱歉,未实现:无法将“侦听器…”扩展为固定长度的参数列表。
错误2:我不能出现在一个常量expression式中。

那么,如何正确地迭代元组呢?

Boost.Fusion是一种可能性:

未经testing的例子:

 struct DoSomething { template<typename T> void operator()(T& t) const { t.do_sth(); } }; tuple<....> t = ...; boost::fusion::for_each(t, DoSomething()); 

我有一个基于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); } 

通常的想法是使用编译时recursion。 实际上,这个想法被用来制作一个types安全的printf,正如原来的元组文件中所提到的那样。

这可以很容易地推广到一个元组for_each

 #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); } 

尽pipe这需要一些努力让FuncT用适合的重载代表元组可能包含的每个types。 如果你知道所有的元组元素都会共享一个通用的基类或者类似的东西,这个效果最好。

使用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; } 

http://coliru.stacked-crooked.com/a/27b3691f55caf271

在C ++ 17中,你可以这样做:

 std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple); 

这已经在Clang ++ 3.9中起作用,使用std :: experimental :: apply。

您需要使用模板元编程,这里用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; } 

在C ++ 0x中,您可以将print_tuple()写成可变参数模板函数。

首先定义一些索引助手:

 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...> {}; 

使用你的函数,你想要应用在每个元组元素上:

 template <typename T> /* ... */ foo(T t) { /* ... */ } 

你可以写:

 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)>()); } 

或者如果foo返回void ,请使用

 std::tie((foo(std::get<I>(ts)), 1) ... ); 

注意:在C ++ 14上, make_index_sequence已经被定义( http://en.cppreference.com/w/cpp/utility/integer_sequence )。

如果您确实需要从左到右的评估顺序,请考虑如下所示:

 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)>()); } 

如果你想使用std :: tuple并且你有支持可变参数模板的C ++编译器,可以尝试下面的代码(用g ++ 4.5testing)。 这应该是你的问题的答案。

 #include <tuple> // ------------- UTILITY--------------- template<int...> struct index_tuple{}; template<int I, typename IndexTuple, typename... Types> struct make_indexes_impl; template<int I, int... Indexes, typename T, typename ... Types> struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> { typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; }; template<int I, int... Indexes> struct make_indexes_impl<I, index_tuple<Indexes...> > { typedef index_tuple<Indexes...> type; }; template<typename ... Types> struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> {}; // ----------- FOR EACH ----------------- template<typename Func, typename Last> void for_each_impl(Func&& f, Last&& last) { f(last); } template<typename Func, typename First, typename ... Rest> void for_each_impl(Func&& f, First&& first, Rest&&...rest) { f(first); for_each_impl( std::forward<Func>(f), rest...); } template<typename Func, int ... Indexes, typename ... Args> void for_each_helper( Func&& f, index_tuple<Indexes...>, std::tuple<Args...>&& tup) { for_each_impl( std::forward<Func>(f), std::forward<Args>(std::get<Indexes>(tup))...); } template<typename Func, typename ... Args> void for_each( std::tuple<Args...>& tup, Func&& f) { for_each_helper(std::forward<Func>(f), typename make_indexes<Args...>::type(), std::forward<std::tuple<Args...>>(tup) ); } template<typename Func, typename ... Args> void for_each( std::tuple<Args...>&& tup, Func&& f) { for_each_helper(std::forward<Func>(f), typename make_indexes<Args...>::type(), std::forward<std::tuple<Args...>>(tup) ); } 

boost :: fusion是另一种select,但它需要自己的元组types:boost :: fusion :: tuple。 让我们更好地坚持标准! 这是一个testing:

 #include <iostream> // ---------- FUNCTOR ---------- struct Functor { template<typename T> void operator()(T& t) const { std::cout << t << std::endl; } }; int main() { for_each( std::make_tuple(2, 0.6, 'c'), Functor() ); return 0; } 

可变模板的力量!

boost的元组提供了帮助函数get_head()get_tail()所以你的帮助函数可能看起来像这样:

 inline void call_do_sth(const null_type&) {}; template <class H, class T> inline void call_do_sth(cons<H, T>& x) { x.get_head().do_sth(); call_do_sth(x.get_tail()); } 

http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html所述;

std::tuple应该是相似的。

实际上,不幸的是, std::tuple似乎没有提供这样的接口,所以前面提到的方法应该可以工作,或者你需要切换到boost::tuple ,它有其他的好处(如io操作符已经提供)。 尽pipe使用gcc的boost::tuple有一些缺点,但它并不接受可变参数模板,但是可能已经解决了,因为我没有在我的机器上安装最新版本的boost。

我可能错过了这列火车,但是这将在这里供将来参考。
这里是我的构build基于这个答案和这个要点 :

 #include <tuple> #include <utility> template<std::size_t N> struct tuple_functor { template<typename T, typename F> static void run(std::size_t i, T&& t, F&& f) { const std::size_t I = (N - 1); switch(i) { case I: std::forward<F>(f)(std::get<I>(std::forward<T>(t))); break; default: tuple_functor<I>::run(i, std::forward<T>(t), std::forward<F>(f)); } } }; template<> struct tuple_functor<0> { template<typename T, typename F> static void run(std::size_t, T, F){} }; 

然后你使用它如下:

 template<typename... T> void logger(std::string format, T... args) //behaves like C#'s String.Format() { auto tp = std::forward_as_tuple(args...); auto fc = [](const auto& t){std::cout << t;}; /* ... */ std::size_t some_index = ... tuple_functor<sizeof...(T)>::run(some_index, tp, fc); /* ... */ } 

可能还有改进的空间。


根据OP的代码,它会变成这样:

 const std::size_t num = sizeof...(T); auto my_tuple = std::forward_as_tuple(t...); auto do_sth = [](const auto& elem){/* ... */}; for(int i = 0; i < num; ++i) tuple_functor<num>::run(i, my_tuple, do_sth);