'Specialice template function

Consider the following template to store simplified SI units:

template < int Mass, int Length, int Time >
class TUnit
{
public:
    // Used type.
typedef float DataType;
    .....
}

For example SI Unit "length" is defined as follows:

typedef TUnit< 0, 1, 0 > Length;

There exits a global generic function to convert DataTypes to TUnits:

template < int Mass, int Length, int Time >
TUnit< Mass, Length, Time > convert( const typename TUnit< Mass, Length, Time >::DataType& src );

And we have a specialized version to convert float to length with e.g. an implicit conversion e.g. from [km] to [m]:

template < >
Tools::DataTypes::Length convert( const Tools::DataTypes::Length::DataType& src );

Now try to convert a float to length:

float f = 1.0;
Length l = ::convert( f )

Now VC2012 fails to compile with error code:

error C2783: could not deduce template argument for 'Mass'
             could not deduce template argument for 'Length'
             could not deduce template argument for 'Time'

To solve this I changed the code to:

float f = 1.0;
Length l = ::convert< 0, 1, 0 >( f )

Nice but this is not what I want :) My preferred syntax would be:

float f = 1.0;
Length l = ::convert< Length >( f )

I think I have to change the signature of the generic template function to something like this:

template < TUnit< int Mass, int Length, int Time > >
TUnit< Mass, Length, Time > convert( const typename TUnit< Mass, Length, Time >::DataType& src );

But of course this syntax is wrong. Any hints to solve this ?



Solution 1:[1]

1. Variant (via template class)

You can not specialize function templates. If you really want to do this, then make a class with a single static function and specialize that class. You can then make the function call the static method of that class.

template < int Mass, int Length, int Time >
TUnit< Mass, Length, Time > convert( const typename TUnit< Mass, Length, Time >::DataType& src ) {
  return Converter<Mass, Length, Time>::call();
}

And then you define the template class:

template < int Mass, int Length, int Time >
struct Converter {
   static TUnit< Mass, Length, Time > call( const typename TUnit< Mass, Length, Time >::DataType& src) {
     [...]
   }
};

And its specializations, e.g. for Length:

template < >
struct Converter<0,1,0> {
   static const int Mass = 0;
   static const int Length = 1;
   static const int Time = 0;

   static TUnit< Mass, Length, Time > call( const typename TUnit< Mass, Length, Time >::DataType& src) {
     [...]
   }
};

2. Variant Overloading

Alternatively, I would recommend to use function overloading instead, which will mostly be equivalent, with that I mean

// NOTE: no `template < >`
Tools::DataTypes::Length convert( const Tools::DataTypes::Length::DataType& src );

Due to the C++ overload mechanism this overloaded function has precedence over the function template convert<Mass, Length, Time>. That is as long as you don't call the function with explicit template arguments.

3. Variant Additional Function Template

If you want to your second approach to work, then I recommend:

template < int Mass_, int Length_, int Time_ >
class TUnit
{
public:
  static const int Mass = Mass_;
  static const int Length = Length_;
  static const int Time = Time_;
[...]

And then

template < class TUnitT >
TUnitT convert( const typename TUnitT::DataType& src ) {
  return convert<TUnitT::Mass, TUnitT::Length, TUnitT::Time>(src);
}

But I would recommend the first approach.

Solution 2:[2]

Sure you can use a template as a template argument. In this case it will be a specialized type so you don't even have to use the template template idiom. Just write something like this:

template < class TUNIT>
TUNIT convert( const typename TUNIT::DataType& src );

template < int M, int A, int B>
TUnit<M,A,B> convert( const typename TUnit<M,A,B>::DataType& src )
{
    typedef TUnit< M,A,B > TUnit_;
    return ::convert<TUnit_>(src);
}

...

typedef TUnit< 0, 1, 0 > Length;

...

float f = 1.0f;
Length l = ::convert< Length >( f );

This will compile fine.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2