Delphi 为什么Format拒绝以XE4开头的过程地址参数

Delphi 为什么Format拒绝以XE4开头的过程地址参数,delphi,Delphi,考虑一下这个计划: {$APPTYPE CONSOLE} uses System.SysUtils; procedure Foo; begin end; type TProcedure = procedure; const FooConst: TProcedure = Foo; var FooVar: TProcedure = Foo; P: Pointer; {$TYPEDADDRESS ON} begin P := @Foo; Writeln(For

考虑一下这个计划:

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

procedure Foo;
begin
end;

type
  TProcedure = procedure;

const
  FooConst: TProcedure = Foo;

var
  FooVar: TProcedure = Foo;
  P: Pointer;

{$TYPEDADDRESS ON}

begin
  P := @Foo;
  Writeln(Format('%p', [P]));
  Writeln(Format('%p', [@FooConst]));
  Writeln(Format('%p', [@FooVar]));
  Writeln(Format('%p', [@Foo]));
  Readln;
end.
此程序编译并在XE3上运行,并生成以下输出:

00419FB8 00419FB8 00419FB8 00419FB8 [dcc32错误]E2250没有可调用的“格式”重载版本 有了这些论点 在XE4、XE5和XE6上,当关闭
$TYPEDADDRESS
时,程序将编译。在XE7上,无论设置了
$TYPEDADDRESS
,程序都无法编译


这是一个编译器错误吗?还是我使用了不正确的语法来获取过程的地址?

我认为新编译器XE7的行为更符合规范,在这种情况下需要显示错误,因为
{$TYPEDADDRESS ON}
强制执行
@
运算符返回一个类型化指针,而Format函数将获取一个非类型化的通用指针作为输入

由于
{$TYPEDADDRESS ON}
的目的是鼓励谨慎使用指针,在编译时捕获不安全的指针赋值,因此如果函数需要一个通用的非类型指针,这是正确的(在这种情况下是有意义的,因为函数的目的是打印它的地址-因此不需要类型化指针来检索它的地址),如果类型化指针被传递,编译器将捕获错误,行为与规范一致

我认为在这种情况下,正确的做法(根据文件)是:

  Writeln(Format('%p', [Addr(FooConst)]));
  Writeln(Format('%p', [Addr(FooVar)]));
因为
Addr
函数总是返回一个非类型指针,这正是
格式
%p
所期望和需要的


我假设在以前的版本中,编译器(在这种情况下)用于执行自动强制转换:
指针(@foocont)
,但由于
{$TYPEDADDRESS ON}
指令,它没有太多意义。

我认为这是一个编译器错误,并提交了一份质量控制报告:

作为解决方法,您可以使用以下任一方法:

  • 使用
    addr()
    而不是
    @
    运算符
  • @FooVar
    @foocont
    转换为
    指针
    ,例如
    指针(@FooVar)

  • Addr(foocont)
    Addr(FooVar)
    在XE6中工作。不受$T指令的影响。现在没有XE7。@LURD
    Addr(…)
    也在XE7中编译并正确运行。这很奇怪,指向编译器错误。谢谢.FWIW,
    指针(@foocont)
    在XE6和XE7中也能正常工作。@LURD是的,我已经观察到了,但在你的例子中,它不是一个函数,而是一个引用函数的新类型……没错。但是在这种情况下,我怎么可以将
    @D
    作为
    格式的参数传递给
    格式,其中D是一个类型为
    Integer
    Double
    的类型常量或变量或者
    string
    或者
    record
    等等?你的论点也应该适用于那里。你似乎还忽略了,在XE7中,程序在
    TYPEDADDRESS
    设置中都会产生编译器错误。我建议你再次阅读这部分问题。我知道这有点棘手,因为我谈论的是很多不同的compilers的行为都集中在一个问题上。如果错误发生在TYPEDADDRESS关闭的情况下,我们应该在QC Embarcadero中打开一个事件,因为这一次不可能是其他任何错误……它应该与TYPEDADDRESS一起工作,或者不与TYPEDADDRESS一起工作,就像它对任何其他类型一样。FWIW我真的不喜欢在出现错误时回答并接受我自己的答案还有其他答案,但我恐怕不同意@aleroot的分析,我真的相信我上面写的是正确的。应该注意的是,这两种解决方法都隐藏了启用
    {$TYPEDADDRESS}的目的
    ,但这是在修复错误之前所能做的最好的事情。@LURD
    @
    在处理过程和过程变量时很奇怪,因为oit被设计为产生非类型指针,如果X是一个变量,@X返回X的地址。(当X是一个过程变量时,特殊规则适用;请参阅如果默认的{$T}编译器指令有效,@X的类型是指针。{$T+}状态下,@X的类型是^T,其中T是X的类型(这种区别对于赋值兼容性很重要,请参阅赋值兼容性).
    @LURD是的,这里计算的是引用的其他文档: [dcc32 Error] E2250 There is no overloaded version of 'Format' that can be called with these arguments
      Writeln(Format('%p', [Addr(FooConst)]));
      Writeln(Format('%p', [Addr(FooVar)]));