Delphi 2006不会在记录方法中允许类型记录的const参数吗?

时间:2013-02-28 21:04:40

标签: delphi

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

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

  • 我开始使用基本记录(TBasicRecord)只有简单的set和print方法(没有运算符),并且传递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关键字相比)。< / p>

2 个答案:

答案 0 :(得分:10)

您应该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. 函数的未来实现可能会更改参数,这会不经意地更改作为参数传递的变量。
  3. 第一点的示例。使用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替换它!)。

答案 1 :(得分:6)

不要用运算符重载中的var替换const。周期。

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

示例:(A + B) * C

如果A,B和C都是TComplex类型,那么这将编译为TComplex.Multiply(TComplex.Add(A, B), C)。如果使用var params声明TComplex.Multiply,则Add的函数结果不能传递给Multiply(因为函数结果是一个中间值,而不是一个存在于特定内存地址的变量),这意味着一个简单的数学表达式,如( A + B)* C不会编译。

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