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 ;)