Проблема, с которой вы столкнулись, связана с T
, обозначающим int
или float
.
Если вы посмотрите на определение класса шаблона, вы заметите, что то же самое T
, который появляется после typename
в части template
, также отображается как параметр для метода Set
.
Это означает, что когда вы говорите о Point<int>
, тогда существует только one Set
определен метод, который занимает два int
.И каждый отдельный Point<T>
будет иметь свой собственный метод Set(T,T)
.
Если вы хотите использовать другой метод Set
, вам нужно объявить шаблон Set
в классе шаблона, это сделановот так:
template <typename T>
class Point
{
public:
template <typename Num>
void Set(Num x, Num y);
};
Обратите внимание, как мне пришлось выбрать другое имя параметра шаблона.
Чтобы решить вашу проблему, вы можете ввести другой метод, для float
, но тогда вам понадобится еще один для double
и long double
... скоро это станет трудным.
Самое простое решение - пойти грубо:
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;
}
Для int
и других это в основном бесполезно, но работает.Для чисел с плавающей запятой мы используем больший доступный тип с плавающей запятой, чтобы избежать потери точности, а затем выполняем округление.
Очевидно, что это не совсем работает, если вдруг мы хотим получить Point<float>
, поэтому нам нужен более умныйрешение, основанное на типе черт.Класс std::numeric_limits<T>
имеет is_integer
, который уточняет, имеем ли мы дело с целочисленным типом.
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;
}
}
}
Я знаю, что глупо использовать if
для чего-то, что можно определить привремя компиляции ... но не волнуйтесь, компилятор достаточно умен, чтобы делать выяснить это во время компиляции и оптимизировать if
и неиспользуемую ветвь в целом;)