David Williams
David Williams

Reputation: 763

Defining traits for templatised classes

I understand how to create type traits and then specialise for a particular class, but in my case I would like to specialise for a class template. The code below does not compile, but the idea is that the specialisation of Traits for MyTemplatisedClass should work for which ever type the user decides to use with MyTemplatisedType.

class Traits
{
public:
    static bool someProperty(void) { return false; }
};

template<typename Type>
class MyTemplatisedClass
{
};

template<typename Type>
template<>
class Traits< MyTemplatisedClass<Type> >
{
public:
    static bool someProperty(void) { return true; }
};

int main(int argc, char* argv[])
{
    std::cout << Traits< MyTemplatisedClass<float> >::someProperty() <<std::endl; //This should be true
    return 0;
}

Is this possible or am I asking too much? According to the compiler the first problem is

error C2989: 'Traits' : class template has already been declared as a non-class template    

Which is correct, but how do I fix this? If it makes any difference I don't need it to work for non-templatised classes, just templatised ones is fine. Edit: Actually it would be nice if it worked for both templatised and non-templatised classes.

Upvotes: 2

Views: 252

Answers (3)

UncleBens
UncleBens

Reputation: 41331

Traits needs to be a template in order to be specialized.

In the specialization drop the line with empty <>: this isn't a template nested within a template or something.

template <typename Type> //can only specialize templates
class Traits
{
public:
    static bool someProperty(void) { return false; }
};

template<typename Type>
class MyTemplatisedClass
{
};

template<typename Type>
//template<>  //Too much here
class Traits< MyTemplatisedClass<Type> >
{
public:
    static bool someProperty(void) { return true; }
};

But if you meant the specialization for any template with one type argument, then that would be:

template < template <class> class SomeTemplatizedType, class Type>
//         ^^^^^^^^^^^^^^^^^^^^^^
//         names a template, not type
class Traits< SomeTemplatizedType<Type> >;
//            ^^^^^^^^^^^^^^^^^^^   ^
//             template name        |
//                               argument

Upvotes: 2

Mike Seymour
Mike Seymour

Reputation: 254431

The problem is that you've declared Traits as a class, not a class template. Just add template<typename> to the definition of Traits, and remove the spurious template<> from the specialisation and it should be fine.

template<typename>             // <--- Add this
class Traits
{
public:
    static bool someProperty(void) { return false; }
};

template<typename Type>
class MyTemplatisedClass
{
};

template<typename Type>
// template<>                 // <--- Remove this
class Traits< MyTemplatisedClass<Type> >
{
public:
    static bool someProperty(void) { return true; }
};

int main(int argc, char* argv[])
{
    std::cout << Traits< MyTemplatisedClass<float> >::someProperty() <<std::endl; //This should be true
    return 0;
}

Upvotes: 1

Puppy
Puppy

Reputation: 146910

The initial class needs to be the "base case", that is, templated to accept any type argument. Then you can worry about what other specializations you'd like to invoke.

template<typename T> class Traits
{
public:
    static bool someProperty(void) { return false; }
};

template<typename Type>
class MyTemplatisedClass
{
};

template<typename Type> class Traits< MyTemplatisedClass<Type> >
{
public:
    static bool someProperty(void) { return true; }
};

In order to actually use this in compile-time computation, you will need to make it an ICE- integral constant expression. A function cannot be constexpr even if, trivially, it's value is knowable at compile-time. As it stands, I cannot do, for example,

template<typename T> std::enable_if<Traits<T>::value, sometype> somefunc();

Upvotes: 1

Related Questions