Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specialize a template without copying and pasting the whole class body?

I wrote a simple class for the moving average which can be used with an AVR.

template<typename T, typename Tsum = int32_t>
class MovingAverage { ... }

But now I want to specialize this class for float without copying and pasting the whole class body and change all T and Tsum to float and that I do not need to use two template parameters. Tsum is the type for the 'sum' variable where all passed values of type T were summed up. If T is 'uint8_t' it is a good idea to use 'uint32_t' for the sum, but for float or double there is no need to use a datatype with higher precision, so I want only one parameter for this purpose. I thought it could work this way:

typedef MovingAverage<float, float> MovingAverage<float>

or this way:

template<>
class MovingAverage<float> : public MovingAverage<float, float> {};

But I was wrong and I found only solutions where I have to write my code twice.

Is there a way to write the class only once and then specialize it this way I prefer? Thanks in advance!

like image 736
user2280245 Avatar asked Apr 14 '13 18:04

user2280245


2 Answers

If you want different default types for Tsum, this should be outsourced to another class which can be specified, for example:

template< typename, typename = void >
struct DefaultSum { using type = int32_t; };

template< typename T >
struct DefaultSum< T, typename std::enable_if<
  std::is_floating_point< T >::value
>::type >
{ using type = T; };

template<typename T, typename Tsum = typename DefaultSum<T>::type >
class MovingAverage { ... }
like image 179
Daniel Frey Avatar answered Oct 06 '22 01:10

Daniel Frey


You could write a simple traits class

// general version
template<typename T>
struct sum_type
{
    typedef int32_t type;
};

// specialized version
template<>
struct sum_type<float>
{
    typedef float type;
};

// repeat for double, the solution from @DanielFrey is even more sophisticated 
// as it specializes all floating point types in one sweep.

and then extract this type in your class template

template<typename T, typename Tsum = typename sum_type<T>::type>
//                                   ^^^^^^^^ <-- dependent type disambiguation
class MovingAverage { ... };

Note that this only works if your MovingAverage has a regularly parameterized implementation. If you are actually doing something special for float (e.g. rewrite expressions to take care of the non-associative character of floating point arithmetic), then you need to do more work.

If you are serious about working with C++ templates, run -not walk- to the nearest bookstore and get the book C++ Templates: The Complete Guide. Section 15.1 has a 15+ page discussion of defining a generic accumulate class template.

like image 28
TemplateRex Avatar answered Oct 06 '22 01:10

TemplateRex