итерация по кортежу
Как я могу перебирать кортеж (используя C++11)? Я попробовал следующее, Но это не работает:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
Ошибка 1: извините, нереализовано: не удается развернуть ' прослушиватель ...- в список аргументов фиксированной длины.
Ошибка 2: я не могу появиться в константное выражение.
Итак, как мне правильно перебирать элементы кортежа?
12 ответов:
импульс.Fusion - это возможность:
непроверенные пример:
struct DoSomething { template<typename T> void operator()(T& t) const { t.do_sth(); } }; tuple<....> t = ...; boost::fusion::for_each(t, DoSomething());
у меня есть ответ, основанный на итерация по кортежу:
#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); }
обычная идея заключается в использовании рекурсии времени компиляции. Фактически, эта идея используется для создания 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); }
хотя это тогда требует некоторых усилий, чтобы иметь
FuncT
представляют что-то с соответствующими перегрузками для каждого типа Кортеж может содержать. Это работает лучше всего, если вы знаете, что все элементы Кортежа будут иметь общий базовый класс или что-то подобное.
Использовать Boost.Хана и родовые лямбды:
#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; }
В C++17 вы можете сделать это:
std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);
Это уже работает в Clang++ 3.9, используя std::experimental:: apply.
вам нужно использовать шаблон метапрограммирования, здесь показано с Boost.Кортеж:
#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.5). Это должно быть ответом на ваш вопрос.
#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-это еще один вариант, но он требует своего собственного типа кортежа:boost::fusion:: tuple. Давайте лучше придерживаться стандарта! Вот такой тест:
#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
что имеет другие преимущества (например, уже предоставленные операторы ввода-вывода). Хотя есть и обратная сторонаboost::tuple
С gcc-он еще не принимает вариативные шаблоны, но это может быть уже исправлено, поскольку у меня нет последней версии boost, установленной на моей машине.
Я, возможно, пропустили этот поезд, но это будет здесь в будущем.
Вот моя конструкция, основанная на этом ответ и этот суть:#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);
вот простой способ 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)...); } }
пример:
#include <iostream> std::tuple<int, char> items; for_each(items, [](const auto& item) { std::cout << item << "\n"; });
в MSVC STL есть функция _For_each_tuple_element (не задокументирована):
#include <tuple> // ... std::tuple<int, char, float> values{}; std::_For_each_tuple_element(values, [](auto&& value) { // process 'value' });
из всех ответов, которые я видел здесь, здесь и здесь мне понравилось @sigidagiспособ итерации лучше всего. К сожалению, его ответ очень многословен, что, на мой взгляд, затеняет присущую ему ясность.
Это моя версия его решения, которое является более кратким и работает с
std::tuple
,std::pair
иstd::array
.template<typename UnaryFunction> void invoke_with_arg(UnaryFunction) {} /** * Invoke the unary function with each of the arguments in turn. */ template<typename UnaryFunction, typename Arg0, typename... Args> void invoke_with_arg(UnaryFunction f, Arg0&& a0, Args&&... as) { f(std::forward<Arg0>(a0)); invoke_with_arg(std::move(f), std::forward<Args>(as)...); } template<typename Tuple, typename UnaryFunction, std::size_t... Indices> void for_each_helper(Tuple&& t, UnaryFunction f, std::index_sequence<Indices...>) { using std::get; invoke_with_arg(std::move(f), get<Indices>(std::forward<Tuple>(t))...); } /** * Invoke the unary function for each of the elements of the tuple. */ template<typename Tuple, typename UnaryFunction> void for_each(Tuple&& t, UnaryFunction f) { using size = std::tuple_size<typename std::remove_reference<Tuple>::type>; for_each_helper( std::forward<Tuple>(t), std::move(f), std::make_index_sequence<size::value>() ); }
демо: coliru
C++14's
std::make_index_sequence
может быть реализована для C++11.