2016-10-15 81 views
5

我是Delphi中的新手。對於我公司所需的項目,我需要將我們現有C++類的一些代碼翻譯成Delphi。其中一些類別是模板,例如:在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的等效代碼。我試圖編寫一個基於上面的C++代碼的Delphi通用類:

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 Operator not applicable to this operand type

AFAIK這個代碼應該工作,我不明白它有什麼問題。所以有人可以向我解釋:

  1. 爲什麼這樣的代碼不能在Delphi中編譯?

  2. 什麼是在Delphi中創建模板類的最正確(最簡單)的方法,該模板類提供了一個Add()函數,儘可能接近C++代碼和上面的用法?

修改是在2016年10月17日

感謝所有的答覆。所以,如果我理解正確,就沒有辦法創建類似於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. 有沒有簡化此代碼的方法,例如,沒有APointF(pt)轉換直接調用pt.m_X的解決方案? (注意我在這裏省略了屬性的實現,即使我認爲它們比直接訪問變量更優雅)
  3. 這個解決方案的性能如何? (也就是說,這個解決方案比直接m_X:= m_X +增加的速度慢得多?)

最後,我看到了在Delphi代碼,它可以實現2種泛型類型的相等比較這樣另一種解決方案:

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. 此類解決方案的性能是否可以接受?

在此先感謝您的答覆

問候

+0

[泛型約束](http://docwiki.embarcadero.com/RADStudio/en/Constraints_in_Generics)不能定義序也不浮動。這是你在這裏看到的,因爲每個'T'沒有算術運算符。 –

+0

這裏用泛型和算術運算符完成一些測試:https://delphihaven.wordpress.com/2011/03/18/generic-arithmetic/。 –

+0

*「Delphi中的泛型不同於C++中的模板或C#中的泛型類型。最值得注意的是,類型參數不能限制爲特定簡單類型,例如Integer,Double,String等。 http://docwiki.embarcadero.com/RADStudio/en/Overloads_and_Type_Compatibility_in_Generics) –

回答

4

德爾福泛型不支持泛型類型的行爲算術運算符。爲了讓編譯器接受代碼,它需要知道每個泛型類型的操作將在實例化時可用。

通用約束允許你告訴類型有哪些功能的編譯器。但是通用約束不允許你告訴編譯器該類型支持arithmetjc操作符。

不幸的是你正在嘗試做的是根本不可能的。當然你可以自己構建框架,可以使用接口等工具來執行算術,但這樣做會放棄性能。如果那是可以接受的,那就很好否則,你最好咬住子彈,避免仿製藥在這裏。

對於C++模板哦。

+0

事實上,C++模板是我從C++中真正想念的兩件事之一。我最近編寫了一個線性代數庫,並實現了實數矩陣和複數矩陣。我必須複製這兩種情況下的所有基本代碼... –

5

德爾福泛型是從C++模板本質上不同,更類似於它們的C#對應。

在C++中,你可以做的模板類型的任何操作,並在模板實例化編譯器檢查您正在執行在模板中的操作可用於您所使用的特定類型的時間。如果沒有,則會出現編譯器錯誤。在Delphi(以及許多其他語言)中,你聲明瞭可能提供一些聲明性約束的泛型類型,而這些約束(基類或接口)決定了你可以對泛型類型執行的操作。在實例化時,唯一的檢查是聲明的類型是否符合約束條件。

可以說,Delphi語言可以添加約束浮點或序類型,但是這將提供一個非常有限的靈活性(改變浮動或整數類型,你可以在一般的情況下使用)。我個人並不認爲這是一個關鍵特徵。

+3

運算符重載意味着算術可以在除您提到的那些之外的其他類型上執行。一個很好的例子是一個複雜的數字類型,但還有更多。因此,對更多功能的渴望不僅限於內置類型。真的,我們希望能夠限制一個類型,要求它定義了特定的操作符。至於它是否是一個關鍵特徵,顯然將取決於你問的對象。我認爲任何嚴肅的數字編程人員都可以將算術與泛型編程相結合。 –

+2

從廣泛的數值處理背景來看,德爾福當然會受益於支持泛型中的算術運算符約束。 –

+0

這是我在Delphi中與C++相比真的很想念的兩件事之一。 –