Reputation: 17258
Given a std::tuple<A, B, ...> foo
is there any generic (templated) function or technique in C++14 to get a new tuple std::tuple<B, ...> bar
which contains all but the first element of foo
? Or, perhaps something in Boost?
I've written a helper function using parameter packs and some template metaprogramming to do this, but I'd love to throw all of that stuff away!
Here's what I'm currently doing. I define a helper function unshift_tuple()
which returns a tuple containing all but the first element of the tuple passed to the function. The implementation of unshift_tuple()
uses a helper function unshift_tuple_with_indices()
which takes a parameter pack containing the tuple indices to extract; the sequential_integer_list
helper type is used to generate the appropriate index list parameter pack using template metaprogramming. Ugly!
#include <tuple>
template <size_t... Integers>
struct integer_list {};
template <size_t N, size_t... Args>
struct sequential_integer_list : sequential_integer_list<N - 1, N - 1, Args...> {};
template <size_t... Args>
struct sequential_integer_list<0, Args...> { typedef integer_list<Args...> type; };
template <typename FirstElement, typename... Elements, size_t... Indices>
static std::tuple<Elements...> unshift_tuple_with_indices(
const std::tuple<FirstElement, Elements...>& tuple,
integer_list<Indices...> index_type)
{
return std::make_tuple(std::get<Indices + 1>(tuple)...);
}
template <typename FirstElement, typename... Elements>
std::tuple<Elements...>
unshift_tuple(const std::tuple<FirstElement, Elements...>& tuple)
{
return unshift_tuple_with_indices(tuple,
typename sequential_integer_list<sizeof...(Elements)>::type());
}
int main(int, char *[])
{
std::tuple<int, std::string, double> foo(42, "hello", 3.14);
std::tuple<std::string, double> bar = unshift_tuple(foo);
}
To be clear, this code works just fine. I just wish very much to delete it (or any portion of it) and use something built-in instead, if possible!
Jarod42 pointed out the existence of std::integer_list
in C++14 which simplifies the implementation to something like:
#include <cstddef>
#include <tuple>
#include <utility>
template <typename T1, typename... T, size_t... Indices>
std::tuple<T...> unshift_tuple_with_indices(
const std::tuple<T1, T...>& tuple, std::index_sequence<Indices...>)
{
return std::make_tuple(std::get<Indices + 1>(tuple)...);
}
template <typename T1, typename... T> std::tuple<T...>
unshift_tuple(const std::tuple<T1, T...>& tuple)
{
return unshift_tuple_with_indices(tuple,
std::make_index_sequence<sizeof...(T)>());
}
Upvotes: 1
Views: 1449
Reputation: 217810
In C++17, you might do
template <typename T1, typename... Ts>
std::tuple<Ts...> unshift_tuple(const std::tuple<T1, Ts...>& tuple)
{
return std::apply([](auto&&, const auto&... args) {return std::tie(args...);}, tuple);
}
std::apply
might be implemented in C++14.
Else, in C++14, there is std::index_sequence
which avoids to write your own version, which simplifies your code to something like:
namespace details
{
template <typename Tuple, size_t... Indices>
auto unshift_tuple_with_indices(
const Tuple& tuple,
std::index_sequence<Indices...> index_type)
{
return std::make_tuple(std::get<Indices + 1>(tuple)...);
}
}
template <typename T, typename... Ts>
std::tuple<Ts...> unshift_tuple(const std::tuple<T, Ts...>& tuple)
{
return details::unshift_tuple_with_indices(tuple, std::index_sequence_for<Ts...>());
}
Upvotes: 6
Reputation: 59
In C++14, there is a class template called std::integer_sequence
that does similar to what you are doing with sequential_integer_list
. You can find the reference in here.
In light of your code, it is possible to reduce and rely on pure meta-programming.
template <typename First, typename ... Elements>
struct unshift_tuple_impl
{
using type = std::tuple<Elements...>;
};
template <typename First, typename ... Elements>
std::tuple<Elements...>
unshift_tuple(const std::tuple<First, Elements...>& tuple)
{
return typename unshift_tuple_impl<First, Elements...>::type{};
}
Upvotes: 1