【发布时间】:2020-06-19 14:55:44
【问题描述】:
考虑以下方法链接的最小示例,其中一个浮点变量由早期方法设置(使用out 参数),然后(使用const 参数)传递给链中后面的方法:
program ChainedConundrum;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
ValueType = Double;
TRec = record
function GetValue(out AOutput: ValueType): TRec;
procedure ShowValue(const AInput: ValueType);
end;
function TRec.GetValue(out AOutput: ValueType): TRec;
begin
AOutput := 394;
Result := Self;
end;
procedure TRec.ShowValue(const AInput: ValueType);
begin
Writeln(AInput);
end;
var
R: TRec;
Value: ValueType = 713;
begin
R.GetValue(Value).ShowValue(Value);
Readln;
end.
我最初希望这会打印浮点数394(以某种格式),但它没有(必然);当我使用 Delphi 10.3.2 的 32 位编译器构建程序时,程序打印出713。使用调试器单步执行程序可确认 Value 的初始预 GetValue 值已传递给 ShowValue。
但是,如果我使用 64 位编译器构建它,则会打印 394。同样,如果我将ValueType 从Double 更改为Int32,我在两个版本中都会得到394。 Int64 在 64 位中产生 394,在 32 位中产生 713。字符串产生更新的值。类就像记录一样工作。然而,与实例方法相反,类方法总是给我更新的值。当然,放弃方法链接 (R.GetValue(Value); R.ShowValue(Value)) 也是如此。
毫不奇怪,如果我将ShowValue 的AInput 参数从const(或未修饰的值)参数更改为var 参数,我总是会得到更新后的值。
我的结论是
- 不允许在这样的方法链中同时设置和传递变量,或者
- 编译器中存在错误。
我的问题是:它是什么?如果不允许,文档在哪里说明了这一点?到目前为止,我还没有找到相关的段落。 (“序列点”这个短语似乎很少出现在 WWW 上的“Delphi”短语附近。)
【问题讨论】:
-
这对我来说就像一个编译器错误。编译器可能会将
Value: ValueType = 713;声明视为常量。它可能不认为GetValue()正在使用out参数,该参数可能会在调用ShowValue()之前改变Value,因此它认为可以直接传递初始常量值而不是当它真的这样做是不行的。奇怪的是,一切都适用于var,因为out的处理方式与非托管类型的var完全相同。你应该report this to Embarcadero 让他们决定。 -
@RemyLebeau:感谢您的回复。我最初的感觉也是,这一定是一个错误。用
GetValue中的var替换out不会改变任何东西(仍然是旧的、错误的值)。 (但是从const更改为ShowValue中的var会使所有操作都按预期工作。这很自然,因为@Value根本不会更改。) -
您是否尝试禁用优化?它绝对看起来像一个优化器错误。
-
所以这个“只”发生在 x64 上的 64 位数据类型上?
Comp和Currency应该显示相同的行为。 -
@AmigoJack:我只在 32 位中看到过,
Single也有这种情况。
标签: delphi parameters undefined-behavior method-chaining