So as it turns out, the C++14 standard makes expanding a tuple, pair, or array, in a function call, very simple.
template <typename Func, typename TupleType, std::size_t... Is> decltype(auto) call_with_tuple_impl( Func&& f, TupleType&& tup, std::index_sequence<Is...>) { return f(std::get<Is>(tup)...); } template <typename Func, typename TupleType> decltype(auto) call_with_tuple( Func&& f, TupleType&& tup) { constexpr auto TUP_SIZE = std::tuple_size< std::decay_t<TupleType>>::value; return call_with_tuple_impl( std::forward<Func>(f), std::forward<TupleType>(tup), std::make_index_sequence<TUP_SIZE>{}); }
Much less painful than its C++11 equivalent. std::integer_sequence
is responsible for most of the ease. Combined with auto
functions, which deduce their own return type, that is.
call_with_tuple
first determines the tuple_size of the tuple-like object passed in. std::make_index_sequence
will result in an index sequence of 0, 1, ..., TUP_SIZE
. The last argument of call_with_tuple_impl
is used to deduce the Is...
pack. finally the mf(std::get<Is>(tup)...)
is the equivalent of mf(std::get<0>(tup), std::get<1>(tup), ..., std::get<TUP_SIZE-1>(tup))
.
The std::decay_t
is necessary because TupType is a universal reference.
I’m using these integer sequences more and more. They’re proving to be extremely useful in flattening complex recursive data structures and logic. C++14 rules