静态类方法和常规例程指针有多兼容?

3

在实际应用中,静态类方法和常规函数指针似乎是兼容的,但编译器并不知道这一点。例如:

type
  TFunc = function(i: Integer): string;

  TMyClass = class
  public
    class function StaticMethod(i: Integer): string; static;
  end;

class function TMyClass.StaticMethod(i: Integer): string;
begin
  Result := '>' + IntToStr(i) + '<';
end;

function GlobalFunc(i: Integer): string;
begin
  Result := '{' + IntToStr(i) + '}';
end;

procedure CallIt(func: TFunc);
begin
  Writeln(func(42));
end;

begin
  CallIt(TMyClass.StaticMethod); //       1a: doesn't compile
  CallIt(GlobalFunc); //                  1b: compiles

  CallIt(@TMyClass.StaticMethod); //      2a: compiles iff $TYPEDADDRESS OFF
  CallIt(@GlobalFunc); //                 2b: compiles iff $TYPEDADDRESS OFF

  CallIt(Addr(TMyClass.StaticMethod)); // 3a: compiles
  CallIt(Addr(GlobalFunc)); //            3b: compiles

  Readln;
end.

如评论中所述,3a和3b都编译通过(其中编译包括在此简单示例中在运行时工作)。当且仅当$TYPEDADDRESSOFF时,2a和2b才会同时编译通过。但是1a/1b有所不同:1b总是编译通过,而1a从不编译通过。这种区别是否是有意设计的?使用3a是否安全或我忽略了任何潜在问题?


1
对我来说,CallIt(@TMyClass.StaticMethod); 运行良好。 - kludg
忘了提一下:我使用 $TYPEDADDRESS ON - Uli Gerhardt
1
@Ulrich - Serg已经给出了答案,即Addr()对{$T+}是免疫的,而@则不是。这在文档中有说明。 - Sertac Akyuz
@Sertac,我知道。 我只是想让我的问题更清楚明了。 - Uli Gerhardt
1个回答

7

对于二进制级别来说,静态类函数和具有相同参数和结果类型的普通函数没有区别——它们是二进制兼容的,所以你的示例是没问题的。当然,它们对编译器来说是不同的类型,因此您需要使用Addr()@来编译您的示例。

Addr()等同于@运算符,但它不受$T编译器指令的影响。如果您打开类型检查,则无法编译您的示例:

{$T+}
begin
  CallIt(@TMyClass.StaticMethod);
  Readln;
end.

[Pascal错误] Project10.dpr(28):E2010不兼容的类型:“TFunc”和“Pointer”

Re 编译器的不同类型:这就是问题所在——为什么它们至少不能赋值兼容?据我所知,在C++中,全局例程和静态方法在适用的情况下是可以互换的。 - Uli Gerhardt
@Ulrich- 这是编译器设计者的选择。我想编译器设计者只有在真正需要为语言添加新功能时才会取消传统的Pascal“强类型”限制(例如泛型)。 - kludg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接