gpt4 book ai didi

delphi - 为什么我不能将我的函数引用分配给匹配的变量? E2555 升高

转载 作者:行者123 更新时间:2023-12-03 14:36:16 26 4
gpt4 key购买 nike

我正在尝试构建一个自定义比较器,它允许将比较函数分配给内部字段。为了简化比较器的创建,我尝试添加一个类似构造函数的类函数 Construct它初始化比较器。

现在,如果我尝试编译以下示例,编译器会显示

[dcc32 Fehler] ConsoleDemo1.dpr(37): E2555 Symbol 'Result' cannot be tracked



我有以下示例代码:
program ConsoleDemo1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
Generics.Collections, Generics.Defaults,
System.SysUtils;

type

TConstFunc<T1, T2, TResult> = reference to function(const Arg1: T1; const Arg2: T2): TResult;

TDemo = class(TComparer<string>)
private
FVar: TConstFunc<string, string, Integer>;
function CompareInternal(const L, R: string): Integer;
public
class function Construct(): TDemo;
function Compare(const L, R: string): Integer; override;
end;

function TDemo.Compare(const L, R: string): Integer;
begin
Result := FVar(L, R);
end;

function TDemo.CompareInternal(const L, R: string): Integer;
begin
Result := AnsiCompareStr(L, R);
end;

class function TDemo.Construct: TDemo;
begin
Result := TDemo.Create();
Result.FVar := Result.CompareInternal;
end;

end.

最佳答案

我不认为这是一个错误。至关重要的是,您已经定义了 TConstFunc作为匿名方法类型。这些是托管的、引用计数的、非常特殊的类型,与常规对象方法完全不同。通过编译器魔法,它们通常是赋值兼容的,但有几个重要的警告。考虑更简洁的:

program Project1;

{$APPTYPE CONSOLE}

type
TFoo = reference to procedure;

TDemo = class
private
FFoo : TFoo;
procedure Foo;
public
class function Construct(): TDemo;
end;

procedure TDemo.Foo;
begin
WriteLn('foo');
end;

class function TDemo.Construct: TDemo;
begin
result := TDemo.Create();
result.FFoo := result.foo;
end;

end.

这也会产生相同的编译器错误 (E2555)。因为成员方法是 procedure of object (对象方法)类型,并且您将其分配给 reference to procedure (匿名方法)类型,这相当于(我怀疑编译器将其扩展为):
class function TDemo.Construct: TDemo;
begin
result := TDemo.Create();
result.FFoo := procedure
begin
result.foo;
end;
end;

编译器不能直接分配方法引用(因为它们是不同的类型),因此(我猜)必须将它包装在一个匿名方法中,该方法隐含地要求捕获 result多变的。 匿名方法无法捕获函数返回值 ,但是 - 只有局部变量可以。

在您的情况下(或者实际上,对于任何 function 类型),由于隐藏了 result 的匿名包装器,甚至无法表达等效项。变量,但我们可以在理论上将其想象为:
class function TDemo.Construct: TDemo;
begin
Result := TDemo.Create();
Result.FVar := function(const L, R : string) : integer
begin
result := result.CompareInternal(L,R); // ** can't do this
end;
end;

正如大卫所展示的,引入一个局部变量(可以被捕获)是一种正确的解决方案。或者,如果您不需要 TConstFunc类型为匿名,您可以简单地将其声明为常规对象方法:
TConstFunc<T1, T2, TResult> = function(const Arg1: T1; const Arg2: T2): TResult of object;



另一个 try catch result 的示例失败:
program Project1;

{$APPTYPE CONSOLE}

type
TBar = reference to procedure;
TDemo = class
private
FFoo : Integer;
FBar : TBar;
public
class function Construct(): TDemo;
end;

class function TDemo.Construct: TDemo;
begin
result := TDemo.Create();
result.FFoo := 1;
result.FBar := procedure
begin
WriteLn(result.FFoo);
end;
end;

end.

这不起作用的根本原因是因为方法的返回值实际上是 var参数和匿名闭包捕获 变量 ,不是 .这是一个关键点。同样,这也是不允许的:
program Project1;

{$APPTYPE CONSOLE}

type
TFoo = reference to procedure;

TDemo = class
private
FFoo : TFoo;
procedure Bar(var x : integer);
end;

procedure TDemo.Bar(var x: Integer);
begin
FFoo := procedure
begin
WriteLn(x);
end;
end;

begin
end.

[dcc32 Error] Project1.dpr(18): E2555 Cannot capture symbol 'x'



在引用类型的情况下,就像在原始示例中一样,您实际上只对捕获引用的值感兴趣,而不是对包含它的变量感兴趣。这并不使它在语法上等效,并且编译器为此目的为您创建一个新变量是不合适的。

我们可以将上面的内容重写为这样,引入一个变量:
procedure TDemo.Bar(var x: Integer);
var
y : integer;
begin
y := x;
FFoo := procedure
begin
WriteLn(y);
end;
end;

这是允许的,但预期的行为会非常不同。在捕获 x的情况下(不允许),我们希望 FFoo将始终写入作为参数传入的任何变量的当前值 xBar ,无论在此期间可能已在何处或何时更改。我们还希望闭包即使在它脱离创建它的任何作用域之后也能使变量保持事件状态。

然而,在后一种情况下,我们期望 FFoo输出 y 的值,这是变量 x 的值因为这是最后一次 Bar被称为。

回到第一个例子,考虑一下:
program Project1;    
{$APPTYPE CONSOLE}
type
TFoo = reference to procedure;
TDemo = class
private
FFoo : TFoo;
FBar : string;
procedure Foo;
public
class function Construct(): TDemo;
end;

procedure TDemo.Foo;
begin
WriteLn('foo' + FBar);
end;

class function TDemo.Construct: TDemo;
var
LDemo : TDemo;
begin
result := TDemo.Create();
LDemo := result;
LDemo.FBar := 'bar';
result.FFoo := LDemo.foo;
LDemo := nil;
result.FFoo(); // **access violation
end;

var
LDemo:TDemo;
begin
LDemo := TDemo.Construct;
end.

这里很清楚:
result.FFoo := LDemo.foo;

我们没有为方法分配正常引用 foo属于 TDemo 的实例存储在 LDemo ,但实际上已经捕获了 变量 LDemo本身,而不是 它当时包含。设置 LDemonil之后自然会产生访问冲突,即使认为它在进行分配时引用的对象实例仍然存在。

这与我们简单定义 TFoo 的行为完全不同。作为 procedure of object而不是 reference to procedure .如果我们这样做了,上面的代码就会像人们天真地预期的那样工作(输出 foobar 到控制台)。

关于delphi - 为什么我不能将我的函数引用分配给匹配的变量? E2555 升高,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34677505/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com