Дизайн похожих типизированных классов - PullRequest
1 голос
/ 24 апреля 2019

Я разрабатываю классы для представления геометрических объектов, таких как векторы.Мне нужно определить методы, такие как сложение, скалярное умножение, скалярное произведение и другие.Координаты вектора могут быть целыми числами или вещественными числами разных размеров, поэтому необходимо дублировать классы, соответствующие этим различным типам.

Например,

class intVector
{
   int X, Y;

   intVector& operator+=(const intVector& A) { … }
   intVector& operator*=(int A) { … }

   float Norm(); // Not int
};

class floatVector
{
   float X, Y;

   floatVector& operator+=(const floatVector& A) { … }
   floatVector& operator*=(float A) { … }

   float Norm(); // Would be double for a doubleVector
};

(мне также нужны бинарные операторы, определенные как функцииа не методы.)

Я хочу избежать / минимизировать дублирование кода, поэтому использование шаблонов кажется естественным подходом.В любом случае, я хочу, чтобы мои классы отображались как обычные классы, а не как шаблоны (один из вариантов - наследовать от шаблонного класса; другой - от специализаций по типу определения).

Кроме того, существует неприятное ограничение: невсе методы имеют смысл для всех типов данных и не должны быть объявлены вообще в некоторых классах, или могут быть особые случаи с типами определенных аргументов.

Во время моих попыток я столкнулся с многочисленными проблемами, такими какнеобходимо добавить множество явных примеров, сложность, чтобы избежать членов, которые не имеют смысла, бросить проблемы между базовыми / производными классами ... делая весь дизайн болезненным.В общем, я трачу больше времени на написание кода с шаблонами, чем без!

У вас были подобные проекты классов?Есть ли классический способ решить эту проблему?

1 Ответ

1 голос
/ 24 апреля 2019

Шаблоны - верный способ справиться с этим.Что вы можете сделать, это добавить перегрузки для различных функций, которые должны вести себя по-разному, и использовать SFINAE , чтобы ограничить их типами, для которых они необходимы.Используя шаблон, мы можем объединить оба класса в общий класс Vector, а затем использовать псевдоним типа , чтобы получить конкретные имена для различных типов.Это выглядело бы как

template<typename T>
class Vector
{
   T X, Y;

   Vector& operator+=(const Vector& A) { … }
   Vector& operator*=(T A) { … }

   template<typename U = T, std::enable_if_t<std::is_integral_v<U>, bool> = true>
   correct_size_floating_point_type<U> Norm() { integer code } 
   template<typename U = T, std::enable_if_t<std::is_floating_point_v<U>, bool> = true>
   U Norm() { floating point code }
};

using intVector = Vector<int>;
using floatVector = Vector<float>;

Где correct_size_floating_point_type - это тип шаблона, который возвращает тип с плавающей точкой правильного размера для предоставленного целочисленного типа.

...