Арифметические операции с родовыми типами в Delphi

Я новичок в Delphi. Для проекта, требуемого моей компанией, мне нужно перевести код из наших существующих классов C ++ в Delphi. Некоторые из этих классов являются шаблонами, такими как:

template <class T>
struct APoint
{
    T m_X;
    T m_Y;

    virtual void Add(T value);
};

template <class T>
void APoint<T>::Add(T value)
{
    m_X += value;
    m_Y += value;
}

Я использую его, например, с помощью этого кода

APoint<float> pt;
pt.m_X = 2.0f;
pt.m_Y = 4.0f;
pt.Add(5.0f);

и это хорошо работает.

Теперь мне нужно написать эквивалентный код для Delphi. Я попытался написать класс Delphi Generic, основанный на коде C ++ выше:

APoint<T> = record
  m_X: T;
  m_Y: T;

  procedure Add(value: T);
end;

procedure APoint<T>.Add(value: T);
begin
  m_X := m_X + value;
  m_Y := m_Y + value;
end;

Однако этот код не компилируется. Я получаю эту ошибку:

E2015 Оператор не применим к этому типу операнда

AFAIK этот код должен работать, и я не понимаю, что с ним не так. Так может кто-нибудь объяснить мне:

  1. Почему такой код не компилируется в Delphi?

  2. Каков правильный (и самый простой) способ в Delphi создать класс шаблона, который обеспечивает Add()функцию, максимально приближенную к коду C ++ и использованию выше?

EDITED 17.10.2016

Спасибо за все отклики. Поэтому, если я правильно понял, нет способа создать шаблон стиля, подобный c ++, потому что Delphi накладывает несколько ограничений, которые не существуют в c ++.

Исходя из этого, я искал обходной путь, чтобы достичь цели, которую я хочу. Я нашел следующее решение:

IPoint<T> = interface
    procedure Add(value: T);
end;

APoint<T> = class(TInterfacedObject, IPoint<T>)
    m_X: T;
    m_Y: T;

    procedure Add(value: T); virtual; abstract;
end;

APointF = class(APoint<Single>)
    destructor Destroy; override;
    procedure Add(value: Single); reintroduce;
end;

destructor APointF.Destroy;
begin
    inherited Destroy;
end;

procedure APointF.Add(value: Single);
begin
    m_X := m_X + value;
    m_Y := m_Y + value;
end;

Я использую его, например, с помощью этого кода

procedure AddPoint;
var
    pt: IPoint<Single>;
begin
    pt := APointF.Create;

    APointF(pt).m_X := 2.0;
    APointF(pt).m_Y := 4.0;
    APointF(pt).Add(5.0);
end;

и это хорошо работает. Однако я считаю, что стиль немного тяжелый, например, необходимость использовать APointF (pt). Итак, в связи с вышеприведенным кодом, мои вопросы:

  1. Является ли это решение хорошим решением? (то есть лучше написать версию каждой записи для каждого типа, который я хочу поддерживать, например, APointF, APointI, APointD, ...)
  2. Есть ли способ упростить этот код, например, решение для вызова pt.m_X напрямую без преобразования APointF (pt)? ( ПРИМЕЧАНИЕ. Здесь я опустил реализацию свойств, даже если я считаю их более элегантными, чем прямой доступ к переменной)
  3. Как насчет выступлений этого решения? (Т.е. это решение значительно медленнее, чем прямое добавление значения m_X: = m_X +?)

Наконец, я увидел другое решение в коде Delphi, где можно реализовать сравнение равенства двух типов общего вида следующим образом:

function APoint<T>.IsEqual(const other: APoint<T>): Boolean;
var
    comparer: IEqualityComparer<T>;
begin
    Result := (comparer.Equals(m_X, other.m_X) and comparer.Equals(m_Y, other.m_Y));
end;

Я попытался прочитать код за сценой, однако я нашел это ужасно сложным. Итак, мои вопросы:

  1. Является ли такое решение лучше, чем предлагаемое выше?
  2. Есть ли аналогичное готовое решение для математических операций?
  3. Являются ли результаты такого решения приемлемыми?

Заранее благодарю за ваши ответы

С уважением

delphi,templates,generics,math,addition,

5

Ответов: 2


Delphi Generics по своей сути отличаются от шаблонов C ++ и напоминают больше своих C # -компонентов.

В C ++ вы можете выполнять любую операцию над типами шаблонов, а во время создания шаблона компилятор проверяет, что операция, которую вы выполняете в шаблоне, доступна для определенного типа, который вы используете. Если нет, вы получите ошибку компилятора.

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

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


4

Дженерики Delphi не поддерживают арифметические операторы, которые действуют на общие типы. Чтобы компилятор мог принять код, ему нужно знать, что каждая операция в родовом типе будет доступна после создания экземпляра.

Общие ограничения позволяют сообщать компилятору, какие возможности имеет тип. Однако общие ограничения не позволяют вам сообщать компилятору, что тип поддерживает операции arithmetjc.

К сожалению, то, что вы пытаетесь сделать, просто невозможно. Конечно, вы можете самостоятельно создавать фреймворки, которые могут использовать инструменты, такие как интерфейсы, для выполнения арифметики, но это приводит к снижению производительности. Если это приемлемо, тогда штраф. В противном случае вы лучше всего кусаете пулю и избегаете дженериков.

Ох для шаблонов C ++.

Дельфы, шаблоны, дженерики, математика, сложение,
Похожие вопросы