在一系列方法中用作输出和(非参考)输入的值

Value used as both output and (non-reference) input in a chain of methods

考虑以下方法链接的最小示例,其中浮点变量由早期方法设置(使用 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。同样,如果我将 ValueTypeDouble 更改为 Int32,我会在两个版本中得到 394Int64 在 64 位中产生 394,在 32 位中产生 713。字符串产生更新后的值。 Classes 就像记录一样工作。 Class 方法,但是,与实例方法相反,总是给我更新的值。当然,放弃方法链接 (R.GetValue(Value); R.ShowValue(Value)) 也是一样的。

毫不奇怪,如果我将 ShowValueAInput 参数从 const(或未修饰的值)参数更改为 var 参数,我总是得到更新值。

我的结论是

  1. 不允许在像这样的方法链中设置和传递变量,或者
  2. 编译器中存在错误。

我的问题是:它是哪个?如果不允许,文档在哪里说明这一点?到目前为止,我还没有找到相关的段落。 (在 WWW 上,短语 "sequence point" 似乎很少出现在短语 "Delphi" 附近。)

在这里或其他地方评论过这个问题的每个人都同意这“感觉像”或“显然”是一个编译器错误。

我在 Embarcadero Jira 创建了问题 RSP-29733

转向可能的解决方法,请注意问题似乎是编译器使用了变量的旧 value。因此,当 value 更改接近变量的使用时,就会出现问题。

但是,变量的地址没有改变,所以如果你通过引用而不是值传递变量,问题就会消失。一种方法是在第二次传递值时使用 var 参数,即使您不需要它,甚至在语义上不需要它。

因此,更自然的方法似乎是使用 const [Ref] 参数:

procedure ShowValue(const [Ref] AInput: ValueType);

这与未修饰的 const 参数具有相同的语义,但强制编译器通过引用传递变量,从而避免了错误。