加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – E2009不兼容的类型:’参数列表不同’

发布时间:2020-12-15 09:32:24 所属栏目:大数据 来源:网络整理
导读:我收到以下错误: E2009 Incompatible types: ‘Parameter lists differ’ 但是我不同意,看看定义,我看不出有什么区别. 这是记录定义: type TFastDiv = record private ... DivideFunction: function (const Buffer: TFastDiv; x: integer): integer; 这是
我收到以下错误:

E2009 Incompatible types: ‘Parameter lists differ’

但是我不同意,看看定义,我看不出有什么区别.

这是记录定义:

type
  TFastDiv = record
  private
    ...
    DivideFunction: function (const Buffer: TFastDiv; x: integer): integer;

这是我想要分配的Mod功能:

function dividefixedi32(const Buffer: TFastDiv; x: integer): integer;
asm

以下分配发出错误:

class operator TFastDiv.Implicit(a: integer): TFastDiv;
begin
  if (a = 0) then begin 
    raise EDivByZero.Create('Setting a zero divider is a division by zero error') 
      at ReturnAddress; 
  end;
  Result.FSign:= Math.sign(a);
  case Result.FSign of
    -1: begin
      SetDivisorI32(Result,a);
      Result.DivideFunction:= dividefixedi32;  <<-- error E2009

我的代码出了什么问题?

SSCCE

unit SSCCE;

interface

uses Math;

type
  TFastDiv = record
  private
    FBuffer: UInt64; // The reciprocal of the divider
    FDivider: integer; // The divider itself (need with modulus etc).
    FSign: TValueSign;
    DivideFunction: function (const Buffer: TFastDiv; x: integer): integer;
    ModFunction: function (const Buffer: TFastDiv; x: integer): integer;
  public
    class operator Implicit(a: integer): TFastDiv;
  end;


implementation

uses SysUtils;

function dividefixedi32(const Buffer: TFastDiv; x: integer): integer; forward;

class operator TFastDiv.Implicit(a: integer): TFastDiv;
begin
  if (a = 0) then begin raise EDivByZero.Create('Setting a zero divider is a division by zero error') at ReturnAddress; end;
  Result.FSign:= Math.sign(a);
  case Result.FSign of
    -1: begin
      //SetDivisorI32(Result,a);
      Result.DivideFunction:= dividefixedi32;
     end; {-1:}
    1: begin
      //SetDivisorU32(Result.FBuffer,a);
    end; {1:}
  end; {case}
  Result.FDivider:= a;
end;

function dividefixedi32(const Buffer: TFastDiv; x: integer): integer;
asm
  mov     eax,edx
  mov     r8d,edx               // x
  mov     r9,rcx                // Buffer
  imul    dword [r9]             // m
  lea     eax,[rdx+r8]          // r8 = r8 or rsi
  mov     ecx,[r9+4]            // shift count
  sar     eax,cl
  sar     r8d,31                // sign(x)
  sub     eax,r8d
  ret
end;

end.

解决方法

首先,一些一般性建议.你的SSCCE很差.它既不短也不自足.这实际上非常重要.使演示代码尽可能短,可以帮助您理解问题.这绝对是这种情况.

这是我对SSCCE的看法:

program soq19147523_version1;

type
  TRecord = record
    data: Integer;
    proc: procedure(const rec: TRecord);
  end;

procedure myproc(const rec: TRecord);
begin
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc; // fail,E2009
end;

begin
end.

这无法用E2009编译.你可以通过多种方式进行编译.例如,删除数据成员会导致编译成功.

program soq19147523_version2;

type
  TRecord = record
    proc: procedure(const rec: TRecord);
  end;

procedure myproc(const rec: TRecord);
begin
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc; // compiles
end;

begin
end.

在XE3中,您可以通过将[ref]属性添加到过程类型的参数来进行编译.为了明确,这在XE3中编译:

program soq19147523_version3;

type
  TRecord = record
    data: Integer;
    proc: procedure(const [ref] rec: TRecord);
  end;

procedure myproc(const [ref] rec: TRecord);
begin
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc; // compiles in XE3,no [ref] in XE2
end;

begin
end.

这为我们提供了关于编译器正在做什么的强有力线索.未修饰的const记录参数通过值或引用传递.如果记录足够小以适合寄存器,它将按值传递.

当编译器处理记录时,它还没有完全确定记录的大小.我猜测在编译器内部有一个包含记录大小的变量.在记录声明完成之前,我认为这个大小变量为零.因此编译器决定记录类型的const参数将通过寄存器中的值传递.遇到myproc过程时,记录的真实大小是已知的.它不适合寄存器,因此编译器识别出不匹配.记录中的类型按值接收其参数,但是为分配提供的参数通过引用传递参数.

实际上,您可以从myproc的声明中删除[ref],程序仍然可以编译.

这也解释了为什么您发现使用var参数导致编译成功.这显然会强制参数通过引用传递.

如果你可以移动到XE3或更高版本,那么解决方案是显而易见的:使用[ref]来强制编译器.

如果你不能移动到XE3那么也许一个无类型的const参数是最好的解决方案.这也会强制编译器通过引用传递参数.

program soq19147523_version4;

type
  TRecord = record
    data: Integer;
    proc: procedure(const rec{: TRecord});
  end;

procedure myproc(const rec{: TRecord});
begin
  Writeln(TRecord(rec).data);
end;

procedure foo;
var
  rec: TRecord;
begin
  rec.proc := myproc;
end;

begin
end.

我在Stack Overflow上发帖的经常读者会知道我是操作符重载值类型记录的主要支持者.我广泛使用此功能,它可以生成高效且高度可读的代码.但是,当你开始用更复杂和相互依赖的类型进行努力时,设计和实现就会崩溃.

这个问题中突出的缺陷是一个很好的例子.期望编译器可以处理这种情况并不罕见.期望类型能够引用自身是非常合理的.

实现让程序员失望的另一个例子是当你希望在该记录中放入记录类型的const时.例如,请考虑以下类型:

type
  TComplex = record
  public
    R,I: Double;
  const
    Zero: TComplex = (R: 0.0,I: 0.0);
  end;

这无法在零声明中编译,而E2086类型’TComplex’尚未完全定义.

另一个限制是类型A不能引用类型B,反之亦然.我们可以为类做出前向声明,但不能为记录做出声明.我知道需要修改编译器实现来支持这一点,但它当然可以实现.

而且还有更多.为什么不能允许继承记录?我不想要多态,我只想继承记录的数据成员和方法.我甚至不需要这是你通过课程获得的行为.那就是我不介意TDerivedRecord不是TBaseRecord.我想要的只是继承成员和函数以避免重复.

可悲的是,在我看来,这是一个90%完成的功能,只是缺少完成它所需要的温柔,爱护.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读