I've implemented a C++14 for_each
for tuple-like objects. It's similar to std::for_each
in that it also returns the functor once it's done. Usage examples:
With a visitor functor:
// visitor functor
struct print {
void operator()(int x) const { std::cout << "int: " << x << '\n'; }
void operator()(double x) const { std::cout << "double: " << x << '\n'; }
};
auto t = std::make_tuple(1, 2, 3.14);
for_each(t, print()); // prints: int: 1
// int: 2
// double: 3.14
With a C++14 generic lambda:
auto t = std::make_tuple(1, 2, 3.14);
for_each(t, [](auto x) { std::cout << x << '\n'; }); // prints: 1
// 2
// 3.14
With a stateful functor:
struct summer {
void operator()(int x) noexcept { sum += x; }
int sum = 0;
};
auto t = std::make_tuple(1, 2, 3, 4, 5);
int sum = for_each(t, summer()).sum; // sum == 15
With a std::array
:
std::array<char, 5> arr = {{'h', 'e', 'l', 'l', 'o'}};
for_each(arr, [](char c) { std::cout << c; }); // prints: hello
std::cout << '\n';
Implementation:
#include <tuple>
#include <type_traits>
#include <utility>
namespace detail {
// workaround for default non-type template arguments
template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
// process the `From::value`-th element
template<typename FromIndex,
typename ToIndex,
typename Tuple,
typename UnaryFunction>
struct for_each_t {
constexpr UnaryFunction&& operator()(Tuple&& t, UnaryFunction&& f) const
{
std::forward<UnaryFunction>(f)(
std::get<FromIndex::value>(std::forward<Tuple>(t)));
return for_each_t<index_t<FromIndex::value + 1>,
ToIndex,
Tuple,
UnaryFunction>()(
std::forward<Tuple>(t), std::forward<UnaryFunction>(f));
}
};
// specialization for empty tuple-likes
template<typename FromIndex, typename Tuple, typename UnaryFunction>
struct for_each_t<FromIndex, index_t<0>, Tuple, UnaryFunction> {
constexpr UnaryFunction&& operator()(Tuple&&, UnaryFunction&& f) const
{
return std::forward<UnaryFunction>(f);
}
};
// specialization for last element
template<typename ToIndex, typename Tuple, typename UnaryFunction>
struct for_each_t<index_t<ToIndex::value - 1>, ToIndex, Tuple, UnaryFunction> {
constexpr UnaryFunction&& operator()(Tuple&& t, UnaryFunction&& f) const
{
std::forward<UnaryFunction>(f)(
std::get<ToIndex::value - 1>(std::forward<Tuple>(t)));
return std::forward<UnaryFunction>(f);
}
};
} // namespace detail
template<typename Tuple, typename UnaryFunction>
constexpr UnaryFunction for_each(Tuple&& t, UnaryFunction&& f)
{
return detail::for_each_t<detail::index_t<0>,
detail::index_t<std::tuple_size<
std::remove_reference_t<Tuple>
>::value>,
Tuple,
UnaryFunction>()(
std::forward<Tuple>(t), std::forward<UnaryFunction>(f));
}
Specific concerns I have:
I'm passing in & out functors by universal reference rather than by value (asFollowing the advice from answerers of this post, I've changed the return type ofstd::for_each
does). I'm concerned whether this can cause problems.for_each
toUnaryFunction
(but note the return types offor_each_t
can remain unchanged).Whether the design can be simplified.
I've seen people do it with
std::index_sequence
. The accepted answer in this post is the shortest version I've seen, but it feels a bit like a hack. Also, it generates longer assembly code than my version does. (My version generates the exact same assembly code as a completely manually expanded version.)
std::index_sequence
). I don't have time this evening to look hard but if nobody has answered by tomorrow I'll dig in. \$\endgroup\$ – Martin York Mar 22 '17 at 0:21