перегруженные функции-члены для определенной специализации шаблона - PullRequest
5 голосов
/ 26 февраля 2011

У меня есть класс tPoint, который будет реализован с различными базовыми типами, поэтому

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

Когда тип T имеет тип int, tPoint<int>, я хочу специальный набор (float, float), чтобы можно было округлять значения перед присваиванием.

Я думал, что со специализацией я мог бы:

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

Таким образом, компилятор жалуется, что в определении класса нет соответствующей функции.

Но если я объявляю в классе Set (float, float), то он говорит, что он уже определен (когда компилируется для T = float)

Надеюсь, я ясно дал понять, какой будет чистый подход к этому, или я делаю что-то не так? спасибо!

Ответы [ 5 ]

5 голосов
/ 26 февраля 2011

Вам нужно специализировать класс, как это:

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;}
};
3 голосов
/ 26 февраля 2011

Проблема, с которой вы столкнулись, связана с 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 и неиспользуемую ветвь в целом;)

0 голосов
/ 26 февраля 2011

Старайтесь не предоставлять реализацию Set () по умолчанию внутри класса.Таким образом, легче специализироваться на плавании.

0 голосов
/ 26 февраля 2011

Ваша лучшая ставка:

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 ...);
};
0 голосов
/ 26 февраля 2011

Используйте boost enable_if для предотвращения версии с плавающей запятой при ее создании с помощью float.

...