Here is a similar and more verbose solution than the formerly accepted one given by T.C., which is maybe a little bit easier to understand (-- it's probably the same as thousand others out there in the net):
template
void for_each(TupleType&&, FunctionType
, std::integral_constant::type >::value>) {}
template::type>::value>::type >
void for_each(TupleType&& t, FunctionType f, std::integral_constant)
{
f(std::get(std::forward(t)));
for_each(std::forward(t), f, std::integral_constant());
}
template
void for_each(TupleType&& t, FunctionType f)
{
for_each(std::forward(t), f, std::integral_constant());
}
Usage (with std::tuple
):
auto some = std::make_tuple("I am good", 255, 2.1);
for_each(some, [](const auto &x) { std::cout << x << std::endl; });
Usage (with std::array
):
std::array some2 = {"Also good", "Hello world"};
for_each(some2, [](const auto &x) { std::cout << x << std::endl; });
DEMO
General idea: as in the solution of T.C., start with an index I=0
and go up to the size of the tuple. However, here it is done not per variadic expansion but one-at-a-time.
Explanation:
The first overload of for_each
is called if I
is equal to the size of the tuple. The function then simply does nothing and such end the recursion.
The second overload calls the function with the argument std::get(t)
and increases the index by one. The class std::integral_constant
is needed in order to resolve the value of I
at compile time. The std::enable_if
SFINAE stuff is used to help the compiler separate this overload from the previous one, and call this overload only if the I
is smaller than the tuple size (on Coliru this is needed, whereas in Visual Studio it works without).
The third starts the recursion with I=0
. It is the overload which is usually called from outside.
EDIT: I've also included the idea mentioned by Yakk to additionally support std::array
and std::pair
by using a general template parameter TupleType
instead of one that is specialized for std::tuple
.
As TupleType
type needs to be deduced and is such a "universal reference", this further has the advantage that one gets perfect forwarding for free. The downside is that one has to use another indirection via typename std::remove_reference::type
, as TupleType
might also be a deduced as a reference type.