1

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)>());
}
TypeIA
  • 16,916
  • 1
  • 38
  • 52
  • 3
    https://codereview.stackexchange.com/ is there for requests for feedback on working code. – François Andrieux Nov 27 '18 at 15:44
  • 3
    [`std::index_sequence`](https://en.cppreference.com/w/cpp/utility/integer_sequence) is available in C++14. – Jarod42 Nov 27 '18 at 15:45
  • 2
    @FrançoisAndrieux True, although I'm not so much looking for feedback on the existing code (although that's welcome) as for a standard library technique to do the same thing. I showed the working code as an MCVE to clearly illustrate exactly what I'm trying to do. – TypeIA Nov 27 '18 at 15:46
  • Seems not ugly written in C++14: [Demo](http://coliru.stacked-crooked.com/a/48edccc8fb84af26) – Jarod42 Nov 27 '18 at 15:58
  • Possible duplicate of ["How to split a tuple?"](https://stackoverflow.com/questions/10626856/how-to-split-a-tuple). – G.M. Nov 27 '18 at 15:59
  • Related to [pushing-and-popping-the-first-element-of-a-stdtuple](https://stackoverflow.com/questions/39101454/pushing-and-popping-the-first-element-of-a-stdtuple) – Jarod42 Nov 27 '18 at 16:01
  • This does indeed appear to be a duplicate of the linked questions. I promise I _did_ search first before asking! Mods, feel free to close as duplicate. Thanks Jarod42 and G.M. – TypeIA Nov 27 '18 at 16:04
  • 1
    Possible duplicate of [How to split a tuple?](https://stackoverflow.com/questions/10626856/how-to-split-a-tuple) – Raedwald Nov 27 '18 at 16:13
  • In C++17, you might do something like `std::apply([](auto&&, const auto&... args) { std::make_tuple(args...) }, t);` (depends how you want to handle references in tuple). – Jarod42 Nov 27 '18 at 16:34

2 Answers2

3

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...>());
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

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{};
}
K. Kiryu
  • 59
  • 3
  • 2
    This appears to create an _empty_ tuple without copying over the elements of the `tuple` passed to `unshift_tuple()` but otherwise the technique seems sound. Thank you! – TypeIA Nov 27 '18 at 16:06
  • It is equivalent to `template std::tuple unshift_tuple(const std::tuple&) { return {};}`. – Jarod42 Nov 27 '18 at 16:45