Trait specialization of templated type

132 views Asked by At

I have a trait to return some information about a class e.g. the number of dimensions:

template< typename T >
struct NumDims;

For a "normal" class this is easy to specialize:

template<>
struct NumDims< MyClass >: std::integral_constant< unsigned, 3 >{};

But I have some heavy templated classes that all have a common static constexpr like:

template< class T, class U, class V, bool x, bool y, bool z >
struct TemplateClass{
    static constexpr unsigned numDims = ...;
}

A specilization looks ugly and errorprone:

template< class T, class U, class V, bool x, bool y, bool z >
struct NumDims< TemplateClass<T,U,V,x,y,z> >{
    static constexpr unsigned value = TemplateClass<T,U,V,x,y,z>::numDims;
}

I tried to use variadic templates:

template< class... T >
struct NumDims< TemplateClass<T...> >{
    static constexpr unsigned value = TemplateClass<T...>::numDims;
}

But this crashes my compiler (gcc 4.8.2):
internal compiler error: in unify_one_argument, at cp/pt.c:15506

Any ideas to do this "the prober" way?

1

There are 1 answers

2
TartanLlama On

Your issue is that you have a mixture of type and non-type parameters in your TemplateClass class template, so you can't have a variadic type parameter. As far as I know, there is currently no way to mix type and non-type arguments like that, short of wrapping the non-type ones in some dummy template.

Fortunately, you can side-step the whole issue and just have a partial specialization which is enabled when the type you pass in has a numDims static member:

template <typename T, typename = void>
struct NumDims;

template<>
struct NumDims< MyClass >: std::integral_constant< unsigned, 3 >{};

template< class T >
struct NumDims< T, void_t<decltype(T::numDims)> >{
    static constexpr unsigned value = T::numDims;
};

This uses void_t which is not yet standard, so you can use a simple implementation like this:

template <typename...>
struct voider { using type = void; };
template <typename... Ts> using void_t = typename voider<Ts...>::type;