【问题标题】:Delphi 2006 wont allow const parameters of type record within record method?Delphi 2006 不允许在记录方法中记录类型的常量参数?
【发布时间】:2013-02-28 21:04:40
【问题描述】:

更新:简单的解决方法。方法/运算符字段之前的数据字段。

今天我尝试用我能做的最简单的例子来重现这个错误。

  • 我从只有简单的设置和打印方法(无运算符)的基本记录 (TBasicRecord) 开始,传递 const x:TBasicBecord 没有问题。

  • 然后我添加了一个一元运算符认为会触发错误,但将记录作为 const 传递仍然没有问题。

  • 然后我添加了一个二元运算符,但仍然没有出现错误。

  • 最后我注意到,在我的简单示例中,我在方法字段之前声明了数据字段,结果证明这就是消除错误所需的全部内容。

我也将我的数据字段设为私有,所以一开始我认为这一定是问题所在,但最终结果证明这无关紧要。唯一不同的是我是否将数据字段放在运算符和方法字段之前。

总的来说,我对这个决议感到满意。就我个人而言,无论如何我总是将数据字段放在首位。有趣的是,反过来这样做似乎并没有引起任何其他问题,只要您不尝试将记录类型作为“const”参数传递到任何地方。


原帖:

以前我一直在使用 Delphi 7,但今天安装了 Delphi 2006 以访问 D7 不支持的操作符方法。

我正在尝试编译此处对先前问题的答复之一中列出的代码(复数实现):Request simple example of how to a TComplexMath class (source included)

以下是相关代码的部分列表:

type
  TComplex = record
  public
    class operator Implicit(const D: Double): TComplex;
    class operator Negative(const C: TComplex): TComplex;
    class operator Equal(const C1, C2: TComplex): Boolean;
    class operator NotEqual(const C1, C2: TComplex): Boolean;
    class operator Add(const C1, C2: TComplex): TComplex;
    class operator Add(const C: TComplex; const D: Double): TComplex;
    class operator Add(const D: Double; const C: TComplex): TComplex;
    class operator Subtract(const C1, C2: TComplex): TComplex;
    class operator Subtract(const C: TComplex; const D: Double): TComplex;
    class operator Subtract(const D: Double; const C: TComplex): TComplex;
    class operator Multiply(const C1, C2: TComplex): TComplex;
    class operator Multiply(const C: TComplex; const D: Double): TComplex;
    class operator Multiply(const D: Double; const C: TComplex): TComplex;
    class operator Divide(const C1, C2: TComplex): TComplex;
    class operator Divide(const C: TComplex; const D: Double): TComplex;
    class operator Divide(const D: Double; const C: TComplex): TComplex;
    function IsZero: Boolean;
    function IsNonZero: Boolean;
    function Conj: TComplex;
    function Sqr: TComplex;
    function Sqrt: TComplex;
    function Mag: Double;
    function SqrMag: Double;
  public
    r: Double;
    c: Double;
  end;

class operator TComplex.Negative(const C: TComplex): TComplex;
begin
  Result.r := -C.r;
  Result.c := -C.c;
end;
---- etc ---

问题是,当我尝试编译此代码时(在 D2006 中),采用 TComplex 类型的 每个 运算符都会给出 E2037 错误:“----”的声明不同于之前的声明。 (其中“---”是操作员名称)。

我的解决方法是从每个 TComplex 参数中删除 const 关键字,然后代码正确地符合(并运行)。我可以保留“const x: Double”参数,编译器没有给出错误,但我必须从所有其他参数中删除“const”。

有谁知道这是否是一些未启用的编译器选项?还是在更高版本的 Delphi 中支持此功能,但在 D2006 中不支持?还是只是我做错了什么?

另外,如果我不能在这里使用 const 参数,那么将 var 替换为 const 会有什么好处(与完全删除 const 关键字相比)。

【问题讨论】:

  • 这看起来像一个编译器错误。我会避免使用 var。这有什么帮助?
  • @jachguate:我真的不同意。 var 强制参数为变量,一方面...
  • @jachguate 但是函数可以修改它。所以按值传递在语义上比通过变量引用更好。知道这里的背景故事,并且作为相关代码的作者,我不同意你的看法。
  • 虽然这是一个完整的问题,但我真的很喜欢 Math387 来处理复数。它是从 DewResearch 免费下载的 Delphi,而且速度非常快(就 x87 asm 代码而言)。我已经使用 SSE 汇编更快地完成了一些特定算法,其中 80 位精度并不那么重要,但如果您需要进行严肃的复杂数学运算,它是一个非常好的库。我已经用它编译到 XE2 没有问题。 dewresearch.com/downloads-site/132-download-area-delphi -- 参考dewresearch.com/help/delphi/mtxvec/…
  • Delphi 2006 有许多与运算符重载相关的错误,这就是其中之一。它在 Delphi 2007 中得到修复。

标签: delphi


【解决方案1】:

您应该const 替换为var。让我解释一下原因。

背景

function Add(a: integer): integer;
begin
  result := a + 5;
end;

返回它的参数 + 5。试试ShowMessage(IntToStr(Add(10)))。您也可以使用a := 10; ShowMessage(IntToStr(Add(a))) 来获得相同的结果。在这两种情况下,传递给函数Add 的东西都是数字10。消息显示15

var 参数的预期用途是这样的:

procedure Add(var a: integer);
begin
  a := a + 5;
end;

var表示参数变量应该通过引用传递;也就是说,只有指向参数变量的指针应该传递给过程/函数。

因此,现在您可以这样做了

a := 10;
Add(a);
ShowMessage(IntToStr(a)); // You get 15

现在你不能甚至做Add(10),因为10根本不是一个变量!

比较,

function Add(a: integer): integer;
begin
  a := a + 5;
  result := a;
end;

不会影响a。所以,

a := 10;
ShowMessage(IntToStr(Add(a))); // You get 15
ShowMessage(IntToStr(a)); // You get 10   

现在,考虑一下这个可怕的功能:

function Add(var a: integer): integer;
begin
  a := a + 5;
  result := a;
end;

这也将返回它的参数 + 5,但它也会影响它的参数(非常奇怪!!),并且除了变量之外你不能传递任何东西作为参数(所以Add(10) 不起作用!!)!

a := 10;
ShowMessage(IntToStr(Add(a))); // You get 15
ShowMessage(IntToStr(a)); // You get 15 (!!!)

那么,const 是什么?好吧,const 大致意思是“尽可能通过引用传递(以加快速度;例如,您不需要复制大型记录),但永远不要接受对参数的任何更改”。因此,const 参数实际上可以作为普通参数使用,只是您无法更改它:

function Add(const a: integer): integer;
begin
  result := a + 5;
end;

工作时

function Add(const a: integer): integer;
begin
  a := a + 5;
  result := a;
end;

甚至不编译!但是你仍然可以使用Add(10)

相关案例

从这个讨论中可以清楚地看出,您不应该将const 替换为var。确实,

  1. 如果您从const 更改为var,您的函数将不再接受文字参数(10)或表达式(Tag + 30SomeFunc(a, b))。 这是一场重要的表演!
  2. 函数的未来实现可能会更改参数,这会意外更改作为参数传递的变量。

第一点示例。使用const 或普通参数:

function Complex(a, b: real): TComplex;
begin
  result.r := a;
  result.c := b;
end;

...

var
  c, d: TComplex;
begin    
  d := -c;                        // Works!
  d := -Complex(10, 20);          // Works!

但是使用var:

var
  c, d: TComplex;
begin    
  d := -c;                        // Works!
  d := -Complex(10, 20);          // [DCC Error] Unit5.pas(262):
                                  // E2015 Operator not applicable to this
                                  // operand type

这也不起作用(var):

var
  a, b, c: TComplex;
begin

  a := -(b + c);

确实,现在Negative 的参数不是变量,而是表达式b + c。所以你输得很惨!

第二点的例子。假设你有一个糟糕的一天,你把Negative的实现吸到了

class operator TComplex.Negative(var C: TComplex): TComplex;
begin
  C.r := -C.r;
  C.c := -C.c;
  result := C;
end;

然后是下面的代码,

var
  c, d: TComplex;
begin

  c := Complex(10, 20);
  d := -c;

  ShowMessage(FloatToStr(c.r));
  ShowMessage(FloatToStr(d.r));

以前会产生消息10-10,现在会突然改变并产生-10-10,这是非常出乎意料的!

结论

因此,您的解决方案是完全删除 const(而不是用 var 替换它!)。

【讨论】:

  • 谢谢安德烈亚斯。是的,我理解上面“suckified” Negative 实现中显示的副作用示例。我知道您需要非常小心地为“in”变量使用 var 参数。
  • @Stuart:是的,会的。我什至举了一个例子。 (好吧,也许不是'记录文字',但仍然是记录值的表达式。)无论如何,我在写这个过长的答案时炸毁了我的部分大脑,所以我现在需要睡觉了! (关于另一件事,不应该将文字或表达式作为var 参数传递。如果它在Delphi 2006 中,并且您开始依赖它,那么升级时会遇到令人不快的惊喜......)
  • 很抱歉消化所有安德烈亚斯的速度有点慢。许多有效点,但在我测试“a := -(b + c) 之前似乎没有一个有效点。是的,确实无法像这样链接运算符是“var”选项的完全失败。感谢您的指点那个。
  • @Stuart:好点子。这当然显得有些多余。但是我刚刚尝试过,如果可以的话,编译器会在Add(TComplex, TComplex) 之前选择Add(TComplex, double)(也就是说,如果参数是文字,则不选择var!)。如果 Add(TComplex, double) 是不可能的,就像在你的例子中一样,它会用于下一个可能的匹配。所以理论上你可以通过让两种可能的“方法”(比如这两种)产生不同的结果来产生高度不可预测的“记录算术”!
  • @Stuart,代码在不必使用隐式往返时效率更高。
【解决方案2】:

不要在运算符重载中将 const 替换为 var。期间。

即使你承诺永远不会修改函数体内的 var 参数(一开始是一个可疑的基础),只是 var 参数的存在会破坏运算符函数的一个非常重要的方面:表达式的组合。运算符函数中的 var 参数使该运算符无法与复合表达式中的其他运算符组合在一起,因为函数结果无法传递给 var 参数。

示例:(A + B) * C

如果 A、B 和 C 都是 TComplex 类型,则编译为 TComplex.Multiply(TComplex.Add(A, B), C)。如果 TComplex.Multiply 使用 var params 声明,则 Add 的函数结果不能传递给 Multiply(因为函数结果是中间值,而不是位于特定内存地址的变量),这意味着像 ( A + B) * C 不会编译。

因此,如果您希望运算符可用于复合表达式,请不要在运算符函数中使用 var 参数。

【讨论】:

  • 谢谢 dthorpe。是的,在 Andreas 之前提出的所有观点中,这确实是最引人注目的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-28
  • 1970-01-01
相关资源
最近更新 更多