3
votes

I am struggling with template specialization for integer parameters, maybe it is simply impossible?

What I tried out:

template< int COUNT, typename REMOVE, typename ...T>
struct RemoveFirstElementsImpl
{
    using Type= typename RemoveFirstElementsImpl<COUNT-1, T...>::Type;
};

template< typename ...T>
struct RemoveFirstElementsImpl<0, T...>
{
    using Type= TypeContainer<T...>;
};


template < int COUNT, typename ... T >
struct RemoveFirstElements
{
    using Type = typename RemoveFirstElementsImpl< COUNT, T...>::Type;
};

Results in

error: partial specialization is not more specialized than the primary template because it replaces multiple parameters with a pack expansion

Then I thought about SFINAE with something like:

template < int COUNT, typename = typename std::enable_if<COUNT==0>::type, typename HEAD, typename ... T >
struct RemoveFirstElements
{
    using Type= TypeContainer<T...>;
};
template < int COUNT, typename = void, typename HEAD, typename ... T >
struct RemoveFirstElements
{
    using Type= RemoveFirstElements<COUNT-1, T...>
};

But I have no idea how to get the combination of parameter packs and default parameters to run.

Maybe I am on the complete wrong way. What I want to achieve is to get a parameter list where the first n parameters are removed from my TypeContainer which is simply an extended std::tuple. I only need the type itself not any parameters and I need only on the types and not on any object.

2
Do not drop typename REMOVE - user2249683
@DieterLücking I am still struggling! What do you mean with your comment. I can't get my code to work with adding a REMOVE again. - Klaus
Look at the answer of @Jarod42 - user2249683
OK, catch the point... ! Thanks - Klaus

2 Answers

4
votes

I think you want:

template<int COUNT, typename ...Ts> struct RemoveFirstElementsImpl;

template<>
struct RemoveFirstElementsImpl<0>
{
    using type = std::tuple<>;
};

template<typename T, typename ...Ts>
struct RemoveFirstElementsImpl<0, T, Ts...>
{
    using type = std::tuple<T, Ts...>;
};

template<int N, typename T, typename ...Ts>
struct RemoveFirstElementsImpl<N, T, Ts...>
{
    using type = typename RemoveFirstElementsImpl<N - 1, Ts...>::type;
};

Live Demo

2
votes

The error says it all:

partial specialization is not more specialized than the primary template because it replaces multiple parameters with a pack expansion

The partial specializations must be more specialized than the primary. In your case, we have one element that is more specialized (0 vs COUNT) and one that is less specialized Ts... vs T, Ts... The specialization be at least as specialized in every argument pair.

So we could add two specializations for your base case:

template <int, typename...> struct RemoveFirstElementsImpl;

// base case 1
template <>
struct RemoveFirstElementsImpl<0>
{
    using type = TypeContainer<>; // prefer lower-case type
};

// base case 2
template <typename T, typename... Ts>
struct RemoveFirstElementsImpl<0, T, Ts...>
{
    using type = TypeContainer<T, Ts...>;
};

// recursive case
template <int COUNT, typename T, typename... Ts>
struct RemoveFirstElementsImpl<COUNT, T, Ts...>
: RemoveFirstElementsImpl<COUNT-1, Ts...>
{ };

That works, but it's unsatisfying. Let's pick a different approach altogether. We could actually pop off elements as we go:

template <int COUNT, typename TC>
struct RemoveFirstElementsImpl
: RemoveFirstElementsImpl<COUNT-1, tail_t<TC>>
{ };

template <typename TC>
struct RemoveFirstElementsImpl<0, TC>
{
    using type = TC;
};

Or we could do it as one iteration with index_sequence:

template <int COUNT, typename... Ts>
struct RemoveFirstElementsImpl
: RemoveFirstElementsImpl2<COUNT, TypeContainer<Ts...>,
                           std::make_index_sequence<sizeof...(Ts) - COUNT>
{ };

with:

template <int COUNT, typename TC, typename Seq>
struct RemoveFirstElementsImpl2;

template <int COUNT, typename TC, size_t... Is>
struct RemoveFirstElementsImpl2<COUNT, TC, std::index_sequence<Is...>>
{
    using type = TypeContainer<get_nth_t<Is+COUNT, TC>...>;
};

Where get_nth_t<N, TC> is a metafunction to be implemented that returns the the Nth type in the given TypeContainer. A sample implementation might be:

template <int N, typename TC>
struct get_nth;

template <int N, typename TC>
using get_nth_t = typename get_nth<N, TC>::type;

template <int N, typename... Ts>
struct get_nth<N, TypeContainer<Ts...>> {
    using type = std::tuple_element_t<N, std::tuple<Ts...>>;
};