TypeIA
TypeIA

Reputation: 17258

Get a new tuple containing all but first element of a tuple

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!


Edit

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

Answers (2)

Jarod42
Jarod42

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

K. Kiryu
K. Kiryu

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

Related Questions