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?
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With