How can you iterate over the elements of an std::tuple?
Asked Answered
S

24

164

How can I iterate over a tuple (using C++11)? I tried the following:

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.

So, how do I correctly iterate over the elements of a tuple?

Stefanstefanac answered 29/7, 2009 at 5:57 Comment(6)
May I ask, how you compile in C++0x? It is not released nor ready as far as I know.Mineral
g++ contains experimental support of some C++0X features, including variadic templates, since version 4.3. Other compilers do the same (with different feature sets, if you want to use them in production, you are back in the 90 with a wide variation of support for bleeding edge things)Cruise
I am using g++ version 4.4 with std=c++0xStefanstefanac
This question needs a C++11 update.Rubbico
@Rubbico now, it needs a C++14 updateRepeater
A C++14 solution can be found on stack echange.Phonetics
Y
31

Boost.Fusion is a possibility:

Untested example:

struct DoSomething
{
    template<typename T>
    void operator()(T& t) const
    {
        t.do_sth();
    }
};

tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());
Yokoyokohama answered 29/7, 2009 at 17:41 Comment(4)
@ViktorSehr AFAICT it doesn't (at least on GCC 4.7.2)? Anyone with a hint?Unbeatable
@ViktorSehr Found the problem: a bug/omission causes the behaviour of fusion to depend on the order of the includes, see Boost ticket #8418 for more detailsUnbeatable
need to use boost::fusion::tuple instead of std::tuple to have this working.Howlond
Under GCC 8.1/mingw-64, I get two warnings for the use of boost::fusion::for_each with std lambda expressions: boost/mpl/assert.hpp:188:21: warning: unnecessary parentheses in declaration of 'assert_arg' [-Wparentheses] failed ************ (Pred::************ boost/mpl/assert.hpp:193:21: warning: unnecessary parentheses in declaration of 'assert_not_arg' [-Wparentheses] failed ************ (boost::mpl::not_<Pred>::************Dunlin
R
149

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.

This can be easily generalized into a for_each for tuples:

#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.

Resistless answered 1/8, 2011 at 5:15 Comment(15)
Thanks for the nice simple example. For C++ beginners looking for background on how this works, see SFINAE and enable_if documentation.Whitted
This could easily be generalized to be a generic for_each. In fact, I did it myself. :-) I think this answer would be more useful if it was already generalized.Rubbico
There, I added the generalization because I actually needed one, and I think it'd be useful for others to see.Rubbico
Note: You might also need versions with const std::tuple<Tp...>&.. If you don't intend to modify tuples while iterating, those const versions will suffice.Precocious
Thanks a lot for the nice solution :-). Actually, in the "for each" function, you can overcome the difficulty using a functor, e.g., struct printer { template<typename T> void operator()(const T& t) const { ... } }; as passing a "templated printer" won't work.Tangible
How would you break from this loop? Or return from a function that uses this loop (it seems return is now scoped to the loop, but does not seem to work as a way to break from it)Zavras
@Zavras The first template inline typename std::enable_if<I == sizeof...(Tp), void>::type breaks from he loop on encountering the last tuple element.Resistless
Yes, but I meant is it possible to break manually before reaching the last element?Zavras
Not as written.. You could make a version with the indexing flipped - start at I=sizeof...(Tp) and count down. Then supply a maximum number of args explicitly. You could also make a version that broke on a tag type, say break_t. Then you would put an object of that tag type in your tuple when you wanted to stop printing. Or you could supply a stop type as a template parm. Obviously you couldn't break at run time.Resistless
This for_each looks like a good solution in most cases. In my case though, I needed to pass a member function as the f. A wrapper like std::mem_fun will not help because it requires specification of the class type I'm trying to call the member function of. Of course, that would not be possible because each item in the tuple has a different type. I had to manually hack through the tuple and call the member function.:( Here is a related post by someone else: stackoverflow.com/questions/43259803/…Dunlin
I realised whilst std::mem_fun is not the key, generic lambdas of C++17 are: auto t = my_tuple(); for_each(t, [](auto& e){e.mf();}); where mf is the name of the member function I'd like to call. In my case, my mf has important side-effects and I pass by non-const reference. YMMV.Dunlin
Thanks for example but it sucks to go through so much trouble and research to use C++ features. It seems more like punishment for using c++ features.Shebashebang
@JaveneCPPMcGowan Yes, template metaprogramming (TMP) is difficult. There is a strong push to make the standard library work in constexpr contexts for sane non-TMP programming. C++20 has most of algorithm and utility done. Even <string> and <vector> are partially constexpr.Resistless
N.B: It appears that std::enable_if< I < sizeof...(Ts), void>::type still bamboozles both the XCode and Doxygen parser, which disables automatic indentation and documentation generation below that line. Use () around the comparison.Pragmatism
Also you don't need inline or void here, it's already inline and void is defaultIllicit
M
147

In C++17, you can use std::apply with fold expression:

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

[Online Example on Coliru]

This solution solves the issue of evaluation order in M. Alaggan's answer.

Mortify answered 5/1, 2019 at 14:43 Comment(7)
Could you explain what is happening here: ((std::cout << args << '\n'), ...); ? The lambda is invoked once with the tuple-elements unpacked as args, but what's up with the double parentheses?Jape
@Jape It expands to a comma expression ((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n')) here.Mortify
Note that in case you want to do things that aren't legal in a comma-expression (such as declaring variables and blocks), you can put all of that into a method and simply call it from within the folded-comma-expression.Newhouse
Is there any difference between auto&&... args and auto&... args?Schoonmaker
@starriet auto&& can catch rvaluesMortify
@Mortify Thanks. Then, using auto&& can catch both rvalues and lvalues, but auto& catch only lvalues? Then auto&& is always better, unless I want to restrict to only lvalues?Schoonmaker
And with a simple shortcut: #define FOR_EACH(tuple, callback) \ apply([](auto&&... args) { \ ((callback(args)), ...); \ }, tuple);Mer
P
43

C++ is introducing expansion statements for 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 here and here).

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;
}
Portis answered 27/6, 2019 at 10:40 Comment(2)
Uuuuuuu. I ♥ THIS!! 🥰🥰🥰Yorkist
Also missed C++23 and needs revision ... github.com/cplusplus/papers/issues/156Tribunal
Y
31

Boost.Fusion is a possibility:

Untested example:

struct DoSomething
{
    template<typename T>
    void operator()(T& t) const
    {
        t.do_sth();
    }
};

tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());
Yokoyokohama answered 29/7, 2009 at 17:41 Comment(4)
@ViktorSehr AFAICT it doesn't (at least on GCC 4.7.2)? Anyone with a hint?Unbeatable
@ViktorSehr Found the problem: a bug/omission causes the behaviour of fusion to depend on the order of the includes, see Boost ticket #8418 for more detailsUnbeatable
need to use boost::fusion::tuple instead of std::tuple to have this working.Howlond
Under GCC 8.1/mingw-64, I get two warnings for the use of boost::fusion::for_each with std lambda expressions: boost/mpl/assert.hpp:188:21: warning: unnecessary parentheses in declaration of 'assert_arg' [-Wparentheses] failed ************ (Pred::************ boost/mpl/assert.hpp:193:21: warning: unnecessary parentheses in declaration of 'assert_not_arg' [-Wparentheses] failed ************ (boost::mpl::not_<Pred>::************Dunlin
H
30

In C++17 you can do this:

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

This already works in Clang++ 3.9, using std::experimental::apply.

Highflier answered 8/5, 2016 at 13:5 Comment(2)
Doesn't this lead to the iteration - i.e. calls of do_something() - occurring in an unspecified order, because the parameter pack is expanded within a function call (), wherein arguments have unspecified ordering? That might be very significant; I'd imagine most people would expect the ordering to be guaranteed to occur in the same order as the members, i.e. as the indices to std::get<>(). AFAIK, to get guaranteed ordering in cases like this, the expansion must be done within {braces}. Am I wrong? This answer puts emphasis on such ordering: https://mcmap.net/q/17480/-template-tuple-calling-a-function-on-each-elementLeash
How do you get correspondance to the tuple elements? Copmared to a structured binding for(auto [i, j], ... that is.Fulfillment
R
30

A more simple, intuitive and compiler-friendly way of doing this in C++17, using 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.

Roughneck answered 16/1, 2019 at 21:13 Comment(0)
R
24

Use Boost.Hana and generic 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

Repeater answered 17/4, 2014 at 20:21 Comment(2)
Please please please don't go using namespace boost::fusion (especially together with using namespace std). Now there's no way to know whether that for_each is std::for_each or boost::fusion::for_eachHighhanded
@Highhanded this was done for terseness here and ADL can handle that without a problem. Besides, it's also function local.Repeater
B
12

Here's an easy C++17 way of iterating over tuple items with just standard library:

#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 be 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):

#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
Bangup answered 11/12, 2017 at 22:27 Comment(0)
D
8

You need to use template metaprogramming, here shown with 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.

Drye answered 29/7, 2009 at 6:54 Comment(0)
L
8

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

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).

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)>());
}
Lorna answered 30/6, 2014 at 0:6 Comment(1)
Should cast the return value of foo to void before invoking operator, to avoid possible pathological operator overloading.Hayrick
O
6

Another option would be to implement iterators for tuples. This has the advantage that you can use a variety of algorithms provided by the standard library and range-based for loops. An elegant approach to this is explained here https://foonathan.net/2017/03/tuple-iterator/. The basic idea is to turn tuples into a range with begin() and end() methods to provide iterators. The iterator itself returns a std::variant<...> which can then be visited using std::visit.

Here some examples:

auto t = std::tuple{ 1, 2.f, 3.0 };
auto r = to_range(t);

for(auto v : r)
{
    std::visit(unwrap([](auto& x)
        {
            x = 1;
        }), v);
}

std::for_each(begin(r), end(r), [](auto v)
    {
        std::visit(unwrap([](auto& x)
            {
                x = 0;
            }), v);
    });

std::accumulate(begin(r), end(r), 0.0, [](auto acc, auto v)
    {
        return acc + std::visit(unwrap([](auto& x)
        {
            return static_cast<double>(x);
        }), v);
    });

std::for_each(begin(r), end(r), [](auto v)
{
    std::visit(unwrap([](const auto& x)
        {
            std::cout << x << std::endl;
        }), v);
});

std::for_each(begin(r), end(r), [](auto v)
{
    std::visit(overload(
        [](int x) { std::cout << "int" << std::endl; },
        [](float x) { std::cout << "float" << std::endl; },
        [](double x) { std::cout << "double" << std::endl; }), v);
});

My implementation (which is heavily based on the explanations in the link above):

#ifndef TUPLE_RANGE_H
#define TUPLE_RANGE_H

#include <utility>
#include <functional>
#include <variant>
#include <type_traits>

template<typename Accessor>
class tuple_iterator
{
public:
    tuple_iterator(Accessor acc, const int idx)
        : acc_(acc), index_(idx)
    {

    }

    tuple_iterator operator++()
    {
        ++index_;
        return *this;
    }

    template<typename T>
    bool operator ==(tuple_iterator<T> other)
    {
        return index_ == other.index();
    }

    template<typename T>
    bool operator !=(tuple_iterator<T> other)
    {
        return index_ != other.index();
    }

    auto operator*() { return std::invoke(acc_, index_); }

    [[nodiscard]] int index() const { return index_; }

private:
    const Accessor acc_;
    int index_;
};

template<bool IsConst, typename...Ts>
struct tuple_access
{
    using tuple_type = std::tuple<Ts...>;
    using tuple_ref = std::conditional_t<IsConst, const tuple_type&, tuple_type&>;

    template<typename T>
    using element_ref = std::conditional_t<IsConst,
        std::reference_wrapper<const T>,
        std::reference_wrapper<T>>;

    using variant_type = std::variant<element_ref<Ts>...>;
    using function_type = variant_type(*)(tuple_ref);
    using table_type = std::array<function_type, sizeof...(Ts)>;

private:
    template<size_t Index>
    static constexpr function_type create_accessor()
    {
        return { [](tuple_ref t) -> variant_type
        {
            if constexpr (IsConst)
                return std::cref(std::get<Index>(t));
            else
                return std::ref(std::get<Index>(t));
        } };
    }

    template<size_t...Is>
    static constexpr table_type create_table(std::index_sequence<Is...>)
    {
        return { create_accessor<Is>()... };
    }

public:
    static constexpr auto table = create_table(std::make_index_sequence<sizeof...(Ts)>{}); 
};

template<bool IsConst, typename...Ts>
class tuple_range
{
public:
    using tuple_access_type = tuple_access<IsConst, Ts...>;
    using tuple_ref = typename tuple_access_type::tuple_ref;

    static constexpr auto tuple_size = sizeof...(Ts);

    explicit tuple_range(tuple_ref tuple)
        : tuple_(tuple)
    {
    }

    [[nodiscard]] auto begin() const 
    { 
        return tuple_iterator{ create_accessor(), 0 };
    }

    [[nodiscard]] auto end() const 
    { 
        return tuple_iterator{ create_accessor(), tuple_size };
    }

private:
    tuple_ref tuple_;

    auto create_accessor() const
    { 
        return [this](int idx)
        {
            return std::invoke(tuple_access_type::table[idx], tuple_);
        };
    }
};

template<bool IsConst, typename...Ts>
auto begin(const tuple_range<IsConst, Ts...>& r)
{
    return r.begin();
}

template<bool IsConst, typename...Ts>
auto end(const tuple_range<IsConst, Ts...>& r)
{
    return r.end();
}

template <class ... Fs>
struct overload : Fs... {
    explicit overload(Fs&&... fs) : Fs{ fs }... {}
    using Fs::operator()...;

    template<class T>
    auto operator()(std::reference_wrapper<T> ref)
    {
        return (*this)(ref.get());
    }

    template<class T>
    auto operator()(std::reference_wrapper<const T> ref)
    {
        return (*this)(ref.get());
    }
};

template <class F>
struct unwrap : overload<F>
{
    explicit unwrap(F&& f) : overload<F>{ std::forward<F>(f) } {}
    using overload<F>::operator();
};

template<typename...Ts>
auto to_range(std::tuple<Ts...>& t)
{
    return tuple_range<false, Ts...>{t};
}

template<typename...Ts>
auto to_range(const std::tuple<Ts...>& t)
{
    return tuple_range<true, Ts...>{t};
}


#endif

Read-only access is also supported by passing a const std::tuple<>& to to_range().

Obsess answered 19/3, 2020 at 14:23 Comment(0)
P
5

If you want to use std::tuple and you have C++ compiler which supports variadic templates, try code bellow (tested with g++4.5). This should be the answer to your question.

#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 is another option, but it requires its own tuple type: boost::fusion::tuple. Lets better stick to the standard! Here is a test:

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

the power of variadic templates!

Pricking answered 19/6, 2011 at 9:39 Comment(2)
I tried your first solution, but it fails with this function on pairs. Any idea why?template <typename T, typename U> void addt(pair<T,U> p) { cout << p.first + p.second << endl; } int main(int argc, char *argv[]) { cout << "Hello." << endl; for_each(make_tuple(2,3,4), [](int i) { cout << i << endl; }); for_each(make_tuple(make_pair(1,2),make_pair(3,4)), addt); return 0; }Maund
It's a shame this answer is written so verbose because I think the way of iterating (for_each_impl) is the most elegant of all the solutions I've seen.Sisco
B
4

In MSVC STL there's a _For_each_tuple_element function (not documented):

#include <tuple>

// ...

std::tuple<int, char, float> values{};
std::_For_each_tuple_element(values, [](auto&& value)
{
    // process 'value'
});
Bangup answered 29/12, 2017 at 4:41 Comment(0)
E
4

Using constexpr and if constexpr(C++17) this is fairly simple and straight forward:

template <std::size_t I = 0, typename ... Ts>
void print(std::tuple<Ts...> tup) {
  if constexpr (I == sizeof...(Ts)) {
    return;
  } else {
    std::cout << std::get<I>(tup) << ' ';
    print<I+1>(tup);
  }
}
Elexa answered 24/7, 2019 at 20:34 Comment(1)
If we have if constexpr, we also have std::apply(), which is easier than using a recursive template.Alberto
K
2

Others have mentioned some well-designed third-party libraries that you may turn to. However, if you are using C++ without those third-party libraries, the following code may help.

namespace detail {

template <class Tuple, std::size_t I, class = void>
struct for_each_in_tuple_helper {
  template <class UnaryFunction>
  static void apply(Tuple&& tp, UnaryFunction& f) {
    f(std::get<I>(std::forward<Tuple>(tp)));
    for_each_in_tuple_helper<Tuple, I + 1u>::apply(std::forward<Tuple>(tp), f);
  }
};

template <class Tuple, std::size_t I>
struct for_each_in_tuple_helper<Tuple, I, typename std::enable_if<
    I == std::tuple_size<typename std::decay<Tuple>::type>::value>::type> {
  template <class UnaryFunction>
  static void apply(Tuple&&, UnaryFunction&) {}
};

}  // namespace detail

template <class Tuple, class UnaryFunction>
UnaryFunction for_each_in_tuple(Tuple&& tp, UnaryFunction f) {
  detail::for_each_in_tuple_helper<Tuple, 0u>
      ::apply(std::forward<Tuple>(tp), f);
  return std::move(f);
}

Note: The code compiles with any compiler supporing C++11, and it keeps consistency with design of the standard library:

  1. The tuple need not be std::tuple, and instead may be anything that supports std::get and std::tuple_size; in particular, std::array and std::pair may be used;

  2. The tuple may be a reference type or cv-qualified;

  3. It has similar behavior as std::for_each, and returns the input UnaryFunction;

  4. For C++14 (or laster version) users, typename std::enable_if<T>::type and typename std::decay<T>::type could be replaced with their simplified version, std::enable_if_t<T> and std::decay_t<T>;

  5. For C++17 (or laster version) users, std::tuple_size<T>::value could be replaced with its simplified version, std::tuple_size_v<T>.

  6. For C++20 (or laster version) users, the SFINAE feature could be implemented with the Concepts.

Kopje answered 5/1, 2019 at 11:5 Comment(0)
A
1

I might have missed this train, but this will be here for future reference.
Here's my construct based on this answer and on this gist:

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

You then use it as follow:

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

    /* ... */
}

There could be room for improvements.


As per OP's code, it would become this:

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);
Adduce answered 6/6, 2015 at 0:51 Comment(0)
S
1

Of all the answers I've seen here, here and here, I liked @sigidagi's way of iterating best. Unfortunately, his answer is very verbose which in my opinion obscures the inherent clarity.

This is my version of his solution which is more concise and works with std::tuple, std::pair and 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>()
    );
}

Demo: coliru

C++14's std::make_index_sequence can be implemented for C++11.

Sisco answered 24/4, 2018 at 7:30 Comment(0)
H
1

Expanding on @Stypox answer, we can make their solution more generic (C++17 onward). By adding a callable function argument:

template<size_t I = 0, typename... Tp, typename F>
void for_each_apply(std::tuple<Tp...>& t, F &&f) {
    f(std::get<I>(t));
    if constexpr(I+1 != sizeof...(Tp)) {
        for_each_apply<I+1>(t, std::forward<F>(f));
    }
}

Then, we need a strategy to visit each type.

Let start with some helpers (first two taken from cppreference):

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template<class ... Ts> struct variant_ref { using type = std::variant<std::reference_wrapper<Ts>...>; };

variant_ref is used to allow tuples' state to be modified.

Usage:

std::tuple<Foo, Bar, Foo> tuples;

for_each_apply(tuples,
               [](variant_ref<Foo, Bar>::type &&v) {
                   std::visit(overloaded {
                       [](Foo &arg) { arg.foo(); },
                       [](Bar const &arg) { arg.bar(); },
                   }, v);
               });

Result:

Foo0
Bar
Foo0
Foo1
Bar
Foo1

For completeness, here are my Bar & Foo:

struct Foo {
    void foo() {std::cout << "Foo" << i++ << std::endl;}
    int i = 0;
};
struct Bar {
    void bar() const {std::cout << "Bar" << std::endl;}
};
Harper answered 31/5, 2020 at 10:25 Comment(1)
My question is why std::apply looks like it does. I think this for_each_apply is straight-forward and does what you want. Ask for standardization of for_each_apply.Delusion
F
0

I have stumbled on the same problem for iterating over a tuple of function objects, so here is one more solution:

#include <tuple> 
#include <iostream>

// Function objects
class A 
{
    public: 
        inline void operator()() const { std::cout << "A\n"; };
};

class B 
{
    public: 
        inline void operator()() const { std::cout << "B\n"; };
};

class C 
{
    public:
        inline void operator()() const { std::cout << "C\n"; };
};

class D 
{
    public:
        inline void operator()() const { std::cout << "D\n"; };
};


// Call iterator using recursion.
template<typename Fobjects, int N = 0> 
struct call_functors 
{
    static void apply(Fobjects const& funcs)
    {
        std::get<N>(funcs)(); 

        // Choose either the stopper or descend further,  
        // depending if N + 1 < size of the tuple. 
        using caller = std::conditional_t
        <
            N + 1 < std::tuple_size_v<Fobjects>,
            call_functors<Fobjects, N + 1>, 
            call_functors<Fobjects, -1>
        >;

        caller::apply(funcs); 
    }
};

// Stopper.
template<typename Fobjects> 
struct call_functors<Fobjects, -1>
{
    static void apply(Fobjects const& funcs)
    {
    }
};

// Call dispatch function.
template<typename Fobjects>
void call(Fobjects const& funcs)
{
    call_functors<Fobjects>::apply(funcs);
};


using namespace std; 

int main()
{
    using Tuple = tuple<A,B,C,D>; 

    Tuple functors = {A{}, B{}, C{}, D{}}; 

    call(functors); 

    return 0; 
}

Output:

A 
B 
C 
D
Flatulent answered 13/11, 2018 at 13:7 Comment(0)
B
0

There're many great answers, but for some reason most of them don't consider returning the results of applying f to our tuple... or did I overlook it? Anyway, here's yet another way you can do that:

Doing Foreach with style (debatable)

auto t = std::make_tuple(1, "two", 3.f);
t | foreach([](auto v){ std::cout << v << " "; });

And returning from that:

    auto t = std::make_tuple(1, "two", 3.f);
    auto sizes = t | foreach([](auto v) {
        return sizeof(v);
    });
    sizes | foreach([](auto v) {
        std::cout << v;
    });

Implementation (pretty simple one)

Edit: it gets a little messier.

I won't include some metaprogramming boilerplate here, for it will definitely make things less readable and besides, I believe those have already been answered somewhere on stackoverflow. In case you're feeling lazy, feel free to peek into my github repo for implementation of both

#include <utility>


// Optional includes, if you don't want to implement it by hand or google it
// you can find it in the repo (link below)
#include "typesystem/typelist.hpp"
// used to check if all return types are void, 
// making it a special case 
// (and, alas, not using constexpr-if 
//    for the sake of being compatible with C++14...) 


template <bool Cond, typename T, typename F>
using select = typename std::conditional<Cond, T, F>::type;


template <typename F>
struct elementwise_apply {
    F f;
};

template <typename F>
constexpr auto foreach(F && f) -> elementwise_apply<F> { return {std::forward<F>(f)}; }


template <typename R>
struct tuple_map {
    template <typename F, typename T, size_t... Is>
    static constexpr decltype(auto) impl(std::index_sequence<Is...>, F && f, T&& tuple) {
        return R{ std::forward<F>(f)( std::get<Is>(tuple) )... };
    }
};

template<>
struct tuple_map<void> {
    template <typename F, typename T, size_t... Is>
    static constexpr void impl(std::index_sequence<Is...>, F && f, T&& tuple) {
        [[maybe_unused]] std::initializer_list<int> _ {((void)std::forward<F>(f)( std::get<Is>(tuple) ), 0)... };
    }
};

template <typename F, typename... Ts>
constexpr decltype(auto) operator| (std::tuple<Ts...> & t, fmap<F> && op) {
    constexpr bool all_void = core::Types<decltype( std::move(op).f(std::declval<Ts&>()) )...>.all( core::is_void );
    using R = meta::select<all_void, void, std::tuple<decltype(std::move(op).f(std::declval<Ts&>()))...>>;
    return tuple_map<R>::impl(std::make_index_sequence<sizeof...(Ts)>{}, std::move(op).f, t);
}

template <typename F, typename... Ts>
constexpr decltype(auto) operator| (std::tuple<Ts...> const& t, fmap<F> && op) {
    constexpr bool all_void = check if all "decltype( std::move(op).f(std::declval<Ts>()) )..." types are void, since then it's a special case
    // e.g. core::Types<decltype( std::move(op).f(std::declval<Ts>()) )...>.all( core::is_void );
    using R = meta::select<all_void, void, std::tuple<decltype(std::move(op).f(std::declval<Ts const&>()))...>>;
    return tuple_map<R>::impl(std::make_index_sequence<sizeof...(Ts)>{}, std::move(op).f, t);
}

template <typename F, typename... Ts>
constexpr decltype(auto) operator| (std::tuple<Ts...> && t, fmap<F> && op) {
    constexpr bool all_void = core::Types<decltype( std::move(op).f(std::declval<Ts&&>()) )...>.all( core::is_void );
    using R = meta::select<all_void, void, std::tuple<decltype(std::move(op).f(std::declval<Ts&&>()))...>>;
    return tuple_map<R>::impl(std::make_index_sequence<sizeof...(Ts)>{}, std::move(op).f, std::move(t));
}

Yeah, that would be much nicer if we were to use C++17

This is also an example of std::moving object's members, for which I'll better refer to this nice brief article

P.S. If you're stuck checking if all "decltype( std::move(op).f(std::declval()) )..." types are void you can find some metaprogramming library, or, if those libraries seem too hard to grasp (which some of them may be due to some crazy metaprogramming tricks), you know where to look

Bookish answered 21/4, 2022 at 15:31 Comment(0)
M
0
template <typename F, typename T>
static constexpr size_t
foreach_in_tuple(std::tuple<T> & tuple, F && do_, size_t index_ = 0)
{
    do_(tuple, index_);
    return index_;
}

template <typename F, typename T, typename U, typename... Types>
static constexpr size_t
foreach_in_tuple(std::tuple<T,U,Types...> & tuple, F && do_, size_t index_ = 0)
{
    if(!do_(tuple, index_))
        return index_;
    auto & next_tuple = reinterpret_cast<std::tuple<U,Types...> &>(tuple);
    return foreach_in_tuple(next_tuple, std::forward<F>(do_), index_+1);
}


int main()
{
    using namespace std;
    auto tup = make_tuple(1, 2.3f, 'G', "hello");

    foreach_in_tuple(tup, [](auto & tuple, size_t i)
    {
        auto & value = std::get<0>(tuple);
        std::cout << i << " " << value << std::endl; 
        // if(i >= 2) return false; // break;
        return true; // continue
    });
}
Muskellunge answered 20/10, 2022 at 23:1 Comment(0)
M
0

Here is a solution based on std::interger_sequence.

As I don't know if my_tuple is constructed from std::make_tuple<T>(T &&...) in your code. It's essential for how to construct std::integer_sequence in the solution below.

(1) if your already have a my_tuple outside your function(not using template<typename ...T>), You can use

[](auto my_tuple)
{
    [&my_tuple]<typename N, N... n>(std::integer_sequence<N, n...> int_seq)
    {
        ((std::cout << std::get<n>(my_tuple) << '\n'), ...);
    }(std::make_index_sequence<std::tuple_size_v<decltype(my_tuple)>>{});
}(std::make_tuple());

(2) if your havn't constructed my_tuple in your function and want to handle your T ...arguments

[]<typename ...T>(T... args)
{
    [&args...]<typename N, N... n>(std::integer_sequence<N, n...> int_seq)
    {
        ((std::cout << std::get<n>(std::forward_as_tuple(args...)) << '\n'), ...);
    }(std::index_sequence_for<T...>{});
}();
Minnich answered 16/12, 2022 at 9:44 Comment(0)
N
0

Try to use this:

struct tuple_traits
{
private:
    template<size_t... I, typename T, typename FUNC>
    static constexpr void __handle(T&& tuple, FUNC&& func, std::index_sequence<I...>)
    {
        (func(std::get<I>(tuple)),...);
    }

public:
    template<typename T, typename FUNC>
    static constexpr void for_each(T&& tuple, FUNC&& func)
    {
        using TupleType = std::remove_reference_t<std::remove_cv_t<T>>;
        __handle(
            std::forward<T>(tuple), 
            std::forward<FUNC>(func), 
            std::make_index_sequence<std::tuple_size<TupleType>::value>{}
        );
    }
};

you can use this like this:

using custom_tuple = std::tuple<int, std::string>;

int main(int argc, char* argv[])
{
    custom_tuple _tuple{ 1, "123" };

    tuple_traits::for_each(
        _tuple,
        [](auto&& elem)
        {
            std::cout << elem << std::endl;
        }
    );

    return 0;
}
Nursling answered 23/5, 2023 at 14:25 Comment(0)
T
-1

boost's tuple provides helper functions get_head() and get_tail() so your helper functions may look like this:

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

as described in here http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html

with std::tuple it should be similar.

Actually, unfortunately std::tuple does not seem to provide such interface, so methods suggested before should work, or you would need to switch to boost::tuple which has other benefits (like io operators already provided). Though there is downside of boost::tuple with gcc - it does not accept variadic templates yet, but that may be already fixed as I do not have latest version of boost installed on my machine.

Trantrance answered 26/8, 2010 at 21:27 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.