Template specialization with a template base class

I am writing a template for which I am trying to provide specialization in a class which itself is a template class. When I use it, I actually initiate it using the derived template classes, so I have something like this:

template<typename T> struct Arg { static inline const size_t Size(const T* arg) { return sizeof(T); } static inline const T* Ptr (const T* arg) { return arg; } }; template<typename T> struct Arg<Wrap<T> > { static inline const size_t Size(const Wrap<T>* arg) { return sizeof(T); } static inline const T* Ptr (const Wrap<T>* arg) { return arg.Raw(); } }; class IntArg: public Wrap<int> { //some code } class FloatArg: public Wrap<float> { //some code } template<typename T> void UseArg(T argument) { SetValues(Arg<T>::Size(argument), Arg<T>::Ptr(&argument)); } UseArg(5); UseArg(IntArg()); UseArg(FloatArg()); 

In all cases, the first version is called. So basically my question is: where did I go wrong and how to make it call the version that returns arg when calling UseArg (5), and the other when calling UseArg (intArg)? Other ways to do something like this (without changing the UseArg interface) are of course welcome.

As a note, the example is a bit simplified, which means that in real code, I am wrapping a few more complex things, and the derived class has some actual operations.

+4
source share
4 answers

I think there are three approaches:

1) Specialize Arg for derived types:

 template <typename T> struct Arg ... template <> struct Arg <IntArg> ... template <> struct Arg <FloatArg> ... // and so on ... 

This sucks because you cannot know in advance what types you will have. Of course, you can specialize as soon as you have these types, but you need to do this with the help of those who implement these types.

2) Do not provide default defaults and specialize in basic types

 template <typename T> struct Arg; template <> struct Arg <int> ... template <> struct Arg <float> ... // and so on... template <typename T> struct Arg <Wrap<T> > ... 

This is not ideal either (depending on how many “core types” you expect to use)

3) Use the IsDerivedFrom trick

 template<typename T> struct Wrap { typedef T type; }; class IntArg: public Wrap<int> {}; class FloatArg: public Wrap<float> {}; template<typename D> class IsDerivedFromWrap { class No { }; class Yes { No no[3]; }; template <typename T> static Yes Test( Wrap<T>* ); // not defined static No Test( ... ); // not defined public: enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) }; }; template<typename T, bool DerivedFromWrap> struct ArgDerived; template<typename T> struct ArgDerived<T, false> { static inline const T* Ptr (const T* arg) { std::cout << "Arg::Ptr" << std::endl; return arg; } }; template<typename T> struct ArgDerived<T, true> { static inline const typename T::type* Ptr (const T* arg) { std::cout << "Arg<Wrap>::Ptr" << std::endl; return 0; } }; template<typename T> struct Arg : public ArgDerived<T, IsDerivedFromWrap<T>::Is> {}; template<typename T> void UseArg(T argument) { Arg<T>::Ptr(&argument); }; void test() { UseArg(5); UseArg(IntArg()); UseArg(FloatArg()); } 

Calling test () outputs (as I understand it, your goal):

 Arg::Size Arg<Wrap>::Size Arg<Wrap>::Size 

Extending it to work with a large number of types, such as Wrap, is possible, but randomly, but it is a trick - you do not need to do a lot of specializations.

It should be mentioned that in my code ArgDerived specializes in IntArg instead of Wrap<int> , so calling sizeof(T) on ArgDerived<T, true> returns the size of IntArg instead of Wrap<int> , but you can change it to sizeof(Wrap<T::type>) if that was your intention.

+4
source

As Nicht noted, the compiler does not seek specialization for any base classes.

Instead of specializing in the Arg class, why don't you overload the UseArg function? Have a boilerplate version that accepts T, and have an overloaded version that accepts Wrap. An overloaded version can expand to a raw pointer as needed.

0
source

Instead of creating Arg, which is a template, you can make it a regular class, and then the OVERLOAD Size and Ptr static elements:

 struct Arg { template<class T> static const size_t Size(const T* arg) { return sizeof(T); } template<class T> static const size_t Size(const Wrap<T>* arg) { return sizeof(T); } template<class T> static const T* Ptr (const T* arg) { return arg; } template<class T> static const T* Ptr (const Wrap<T>* arg) { return arg->Raw(); } }; 

EDIT: Well, that won't work, I just checked ...

0
source

typedef Wrap<int> IntArg; instead of output will solve your problem.

In fact, the compiler is not looking for specialization with base classes. Consider a simple example:

 class A { }; class B: public A { }; template<class T> void f(T const &) { std::cout << "T" << std::endl; } template<> void f<A>(A const&) { std::cout << "A" << std::endl; } int main() { f(A()); f(B()); } 

He is typing "AT."

-one
source

All Articles