Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template specialization for special value of integer parameter

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.

like image 847
Klaus Avatar asked Apr 11 '26 04:04

Klaus


2 Answers

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

like image 173
Jarod42 Avatar answered Apr 12 '26 17:04

Jarod42


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...>>;
};
like image 39
Barry Avatar answered Apr 12 '26 18:04

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!