How to iterate over a std::tuple in C++ 11
c++, c++11, tuples
Solution
Here is an attempt to break down iterating over a tuple into component parts.
First, a function that represents doing a sequence of operations in order. Note that many compilers find this hard to understand, despite it being legal C++11 as far as I can tell:
template<class... Fs>
void do_in_order( Fs&&... fs ) {
int unused[] = { 0, ( (void)std::forward<Fs>(fs)(), 0 )... }
(void)unused; // blocks warnings
}
Next, a function that takes a `std::tuple`, and extracts the indexes required to access each element. By doing so, we can perfect forward later on.
As a side benefit, my code supports `std::pair` and `std::array` iteration:
template<class T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes( T const& )
{ return {}; }
The meat and potatoes:
template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
do_in_order( [&]{ f( get<Is>(std::forward<Tuple>(tup)) ); }... );
}
and the public-facing interface:
template<class Tuple, class F>
void for_each( Tuple&& tup, F&& f ) {
auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f) );
}
while it states `Tuple` it works on `std::array`s and `std::pair`s. It also forward the r/l value category of said object down to the function object it invokes. Also note that if you have a free function `get<N>` on your custom type, and you override `get_indexes`, the above `for_each` will work on your custom type.
As noted, `do_in_order` while neat isn't supported by many compilers, as they don't like the lambda with unexpanded parameter packs being expanded into parameter packs.
We can inline `do_in_order` in that case
template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
int unused[] = { 0, ( (void)f(get<Is>(std::forward<Tuple>(tup)), 0 )... }
(void)unused; // blocks warnings
}
this doesn't cost much verbosity, but I personally find it less clear. The shadow magic of how `do_in_order` works is obscured by doing it inline in my opinion.
`index_sequence` (and supporting templates) is a C++14 feature that can be written in C++11. Finding such an implementation on stack overflow is easy. A current top google hit is a decent O(lg(n)) depth implementation, which if I read the comments correctly may be the basis for at least one iteration of the actual gcc `make_integer_sequence` (the comments also point out some further compile-time improvements surrounding eliminating `sizeof...` calls).
Alternatively we can write:
template<class F, class...Args>
void for_each_arg(F&&f,Args&&...args){
using discard=int[];
(void)discard{0,((void)(
f(std::forward<Args>(args))
),0)...};
}
And then:
template<size_t... Is, class Tuple, class F>
void for_each( std::index_sequence<Is...>, Tuple&& tup, F&& f ) {
using std::get;
for_each_arg(
std::forward<F>(f),
get<Is>(std::forward<Tuple>(tup))...
);
}
Which avoids the manual expand yet compiles on more compilers. We pass the `Is` via the `auto&&i` parameter.
In C++1z we can also use `std::apply` with a `for_each_arg` function object to do away with the index fiddling.
Problem
I have made the following tuple: I want to know how should I iterate over it? There is `tupl_size()`, but reading the documentation, I didn't get how to utilize it. Also I have search SO, but questions seem to be around `Boost::tuple` . ``` auto some = make_tuple("I am good", 255, 2.1); ```