今天我尝试使用我能做的最简单的例子来重现这个bug.
我开始使用只有简单的set和print方法(没有运算符)的基本记录(TBasicRecord),并且传递const x:TBasicBecord没有问题.
然后我添加了一个一元运算符,认为会触发错误,但在将记录作为const传递时仍然没有问题.
然后我添加了一个二元运算符,但仍然没有出现错误.
最后我注意到在我的简单示例中,我已经在方法字段之前声明了数据字段,结果证明这是将bug设置为静音所需的全部内容.
我也将我的数据字段设为私有,所以起初我认为这一定是问题所在,但最终结果却是无关紧要的.唯一有意义的是我是否在操作符和方法字段之前放置了数据字段.
总的来说,我对这个决议很满意.就个人而言,无论如何我总是把数据字段放在第一位.有趣的是,反过来做它似乎没有引起任何其他问题,只要你不尝试将记录类型作为"const"参数传递到任何地方.
原始发布:
以前我一直在使用Delphi 7,但今天安装了Delphi 2006来获取D7不支持的操作员方法.
我试图在这里回复一个早期问题的回复中列出的代码(复数实现):请求如何使用TComplexMath类的简单示例(包含源代码)
以下是相关代码的部分列表:
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 ---
Run Code Online (Sandbox Code Playgroud)
问题是,当我尝试编译此代码时(在D2006中),每个采用TComplex类型的运算符都会产生错误E2037:"----"的声明与前一个声明不同.(其中"---"是运营商名称).
我的工作是从每个TComplex参数中删除const关键字,然后代码正确地符合(并运行).我可以保留"const x:Double"参数,编译器没有给出错误,但我必须从所有其他参数中删除"const".
有谁知道这是否是一些未启用的编译器选项?或者这是在Delphi的更高版本中支持的东西,而不是D2006?或者只是我做错了什么?
另外,如果我不能在这里使用const参数,那么将var替换为const是否有任何优势(与仅删除const关键字相比).
And*_*and 10
你应该不是取代const的var.让我解释一下原因.
function Add(a: integer): integer;
begin
result := a + 5;
end;
Run Code Online (Sandbox Code Playgroud)
返回其参数+ 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;
Run Code Online (Sandbox Code Playgroud)
var表示参数变量应该通过引用传递; 也就是说,只应将指向参数变量的指针传递给过程/函数.
因此,现在你可以做到
a := 10;
Add(a);
ShowMessage(IntToStr(a)); // You get 15
Run Code Online (Sandbox Code Playgroud)
现在,你不能连做Add(10),因为10不是一个变量在所有!
比较,
function Add(a: integer): integer;
begin
a := a + 5;
result := a;
end;
Run Code Online (Sandbox Code Playgroud)
不会影响a.所以,
a := 10;
ShowMessage(IntToStr(Add(a))); // You get 15
ShowMessage(IntToStr(a)); // You get 10
Run Code Online (Sandbox Code Playgroud)
现在,考虑一下这个可怕的功能:
function Add(var a: integer): integer;
begin
a := a + 5;
result := a;
end;
Run Code Online (Sandbox Code Playgroud)
这也将返回它的参数+ 5,但它也会影响它的参数(非常出色地!!),你不能传递任何东西,但变量作为参数(所以Add(10)不会工作!!)!
a := 10;
ShowMessage(IntToStr(Add(a))); // You get 15
ShowMessage(IntToStr(a)); // You get 15 (!!!)
Run Code Online (Sandbox Code Playgroud)
那么,是什么const?好吧,const粗略地意味着"如果可能的话,通过引用传递(加速;例如,你不需要复制大型记录),但绝不接受对参数的任何更改".因此,一个const参数有效地作为正常参数起作用,除了你不能改变它:
function Add(const a: integer): integer;
begin
result := a + 5;
end;
Run Code Online (Sandbox Code Playgroud)
同时工作
function Add(const a: integer): integer;
begin
a := a + 5;
result := a;
end;
Run Code Online (Sandbox Code Playgroud)
甚至没有编译!但你仍然可以做到Add(10).
从这个讨论,应该清楚的是,你不应该取代const的var.确实,
const为var,则函数不再接受文字(10)或表达式(Tag + 30或SomeFunc(a, b))的参数.这是一个主要的表演者!第一点的例子.使用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!
Run Code Online (Sandbox Code Playgroud)
但使用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
Run Code Online (Sandbox Code Playgroud)
这也不起作用(with var):
var
a, b, c: TComplex;
begin
a := -(b + c);
Run Code Online (Sandbox Code Playgroud)
实际上,现在的论点Negative不是变量,而是表达式b + c.所以你输了很多!
第二点的例子.假设你有一个糟糕的一天,你可以实现Negativeto
class operator TComplex.Negative(var C: TComplex): TComplex;
begin
C.r := -C.r;
C.c := -C.c;
result := C;
end;
Run Code Online (Sandbox Code Playgroud)
那么下面的代码,
var
c, d: TComplex;
begin
c := Complex(10, 20);
d := -c;
ShowMessage(FloatToStr(c.r));
ShowMessage(FloatToStr(d.r));
Run Code Online (Sandbox Code Playgroud)
这曾经导致邮件10和-10,会突然发生变化,产量-10,-10,这是非常意外!
因此,您的案例中的解决方案只是const完全删除(而不是替换它var!).
不要在运算符重载中用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.