<utility>
If you start playing with parameter packs [2.3.3.0] and tuples [2.5.8], you will soon encounter a case where you will need to so something “for each item in the tuple”.
A typical use case consists in calling a function with its arguments taken from a tuple, something we already tried in the previous pages [2.5.8.0], in an argument-bindings example. Go check, and see how much code we wrote to store an sequence of integers in a type.
Starting with C++14, this code can be replaced by std::integer_sequence<typename T, T... Ints> and std::make_integer_sequence<typename T, T N>. Writing a function that runs a function with its arguments taken from a tuple is approximately as simple as this:
// The call_helper will help us to get the elements of a tuple, because we // cannot get them by type due to possibly duplicate types. template<typename IntegerSequence> struct call_helper; template<unsigned... I> struct call_helper<std::integer_sequence<std::size_t, I...>> { template<typename F, typename... Args> static void call(F&& function, std::tuple<Args...>&& arguments) { // Here we unpack I... to get the arguments from the tuple. // See [2.3.3]. // // Note that it does not work with member functions, as they require another // call syntax. This is still left as an exercise for the reader. function(std::get<I>(arguments)...); } }; template<typename F, typename... Args> decltype(auto) apply(F&& function, std::tuple<Args...>&& arguments) { return call_helper < std::make_integer_sequence<std::size_t, sizeof...(Args)> >::call(std::forward<F>(function), std::move(arguments)); }
Isn’t it clean? Hold on, we’ll soon be able to remove this code too [??]4!
4And it will be for the best, as this example code misses many features, like calling member functions, accepting a const tuple as argument, and probably more.