Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing std::integer_sequence as template parameter to a meta function

How can I pass an std::integer_sequence as a template parameter to a meta function (i.e. not a function template)?

Given e.g. the following use case (but not limited to it):

I want to use a integer sequence to remove the last N types from a parameter pack. I thought I could use selector from this SO question, but I fail to pass the integer sequence to this meta function.

#include <tuple>
#include <utility>

template <typename T, std::size_t... Is>
struct selector
{
    using type = std::tuple<typename std::tuple_element<Is, T>::type...>;
};

template <std::size_t N, typename... Ts>
struct remove_last_n
{
    using Indices = std::make_index_sequence<sizeof...(Ts)-N>;  
    using type = typename selector<std::tuple<Ts...>, Indices>::type; // fails
};

int main()
{
    using X = remove_last_n<2, int, char, bool, int>::type;
    static_assert(std::is_same<X, std::tuple<int, char>>::value, "types do not match");
}

compiler error

main.cpp:15:55: error: template argument for non-type template parameter must be an expression

using type = typename selector<std::tuple<Ts...>, Indices>::type; // fails

                                                  ^~~~~~~

main.cpp:5:38: note: template parameter is declared here

template <typename T, std::size_t... Is>

live on coliru

How would I pass the integer sequence?

like image 720
m.s. Avatar asked Aug 08 '15 12:08

m.s.


2 Answers

You need to (partially) specialize selector so that indices are deduced from std::index_sequence:

#include <tuple>
#include <utility>
#include <type_traits>

template <typename T, typename U>
struct selector;

template <typename T, std::size_t... Is>
struct selector<T, std::index_sequence<Is...>>
{
    using type = std::tuple<typename std::tuple_element<Is, T>::type...>;
};

template <std::size_t N, typename... Ts>
struct remove_last_n
{
    using Indices = std::make_index_sequence<sizeof...(Ts)-N>;  
    using type = typename selector<std::tuple<Ts...>, Indices>::type;
};

int main()
{
    using X = remove_last_n<2, int, char, bool, int>::type;
    static_assert(std::is_same<X, std::tuple<int, char>>::value, "types do not match");
}

DEMO

like image 66
Piotr Skotnicki Avatar answered Nov 12 '22 08:11

Piotr Skotnicki


For a use case this simple, you can also write the metafunction as a function template instead.

template<class...> class wrapper{};

template <typename T, std::size_t... Is>
std::tuple<typename std::tuple_element<Is, T>::type...>
    selector_impl(wrapper<T, std::index_sequence<Is...>>);    

template <std::size_t N, typename... Ts>
struct remove_last_n
{
    using Indices = std::make_index_sequence<sizeof...(Ts)-N>;  
    using type = decltype(selector_impl(wrapper<std::tuple<Ts...>, Indices>()));
};

Incidentally, the tuple_element implementation of selector is generally quite inefficient, because the number of recursive template instantiations required is quadratic. This answer shows one way to make the number of template instantiations required linear in the number of types in the list.

like image 1
T.C. Avatar answered Nov 12 '22 08:11

T.C.