开发者

overloaded member functions for a particular template specialisation

开发者 https://www.devze.com 2023-02-13 10:00 出处:网络
I have a class tPoint that would be implemented having different base types so template<typename T>class tPoint{

I have a class tPoint that would be implemented having different base types so

template<typename T>class tPoint{
   T x;
   T y;
public:
   void Set(T ix, T iy){x=ix;y=iy;}
};

When the type T is int, tPoint<int>, I want a special Set(float, float) so I can round the values before assignation.

I thought that with specialisation I could:

template<> void tPoint<int开发者_开发问答>::Set(float ix,float iy){x=ix+.5; y=iy+.5;}

This way the compiler complains that there's no matching function in the class definition.

But if I declare in the class Set(float,float) then it says that's already defined (when it compiles for T =float)

I hope I made myself clear, what would be a clean approach to this or I'm doing something wrong? thanks!


You need to specialize the class, like this:

template<> class tPoint<int>{
   int x;
   int y;
public:
   void Set(int ix, int iy){x=ix;y=iy;}
   void Set(float ix, float iy){x = ix+0.5; y = iy+0.5;}
};


The issue you're facing has to do with T standing either for int or float.

If you look at the template class definition, you'll remark that the same T that appears after typename in the template part also appears as parameter to the Set method.

This means that when you speak of Point<int> then there is only one Set method defined, which takes two int. And each different Point<T> will have its own Set(T,T) method.

If you wish for a different Set method, then you need to declare a template Set within the template class, this is done like so:

template <typename T>
class Point
{
public:
  template <typename Num>
  void Set(Num x, Num y);
};

Note how I had to chose a different template parameter name.


In order to solve your problem, you could introduce another method, for float, but then you'd have to have another for double, and long double... it's soon going to become difficult.

The simplest solution, is to go bruteforce:

template <typename Integral>
template <typename Num>
void Point<Integral>::Set(Num x, Num y)
{
  this->x = long double(x) + 0.5;
  this->y = long double(y) + 0.5;
}

For int and al, it's mostly useless but works. For floating points, we use the bigger floating point type available to avoid losing precision, and then perform the rounding.

Obviously, it does not quite work if suddenly we want a Point<float>, therefore we need a smarter solution, based on type traits. The class std::numeric_limits<T> has a is_integer which precises whether or not we are dealing with an integral type.

template <typename T>
template <typename Num>
void Point<T>::Set(Num x, Num y)
{
  if (std::numeric_limits<T>::is_integer &&
     !std::numeric_limits<Num>::is_integer)
  {
    this->x = x + 0.5;
    this->y = y + 0.5;
  }
  else
  {
    this->x = x;
    this->y = y;
  }
  }
}

I know it seems stupid to use a if for something that could be determined at compile-time... but don't worry, the compiler is smart enough to do figure it out at compile-time and optimize away the if and the unused branch altogether ;)


Use boost enable_if to prevent the float version when instanciated with float.


your best bet is:

template<typename T>class tPoint{
   T x;
   T y;
public:
   void Set(T ix, T iy) { set_impl(boost::type<T>(), x, y); }
private:
   void set_impl(boost::type<int>, float ...);
   template<typename U>
   void set_impl(boost::type<U>, T ...);
};


Try not to provide the default implementation of Set() inside the class. That way, it's easier to specialize for float.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号