E2009不兼容类型:'参数列表不同' [英] E2009 Incompatible types: 'Parameter lists differ'

查看:368
本文介绍了E2009不兼容类型:'参数列表不同'的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我收到以下错误:


E2009不兼容的类型:'参数列表不同'


< blockquote>

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





这是记录定义:

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

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

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

以下分配发生错误:

 类运算符TFastDiv.Implicit(a:integer):TFastDiv; 
begin
if(a = 0)then begin
raise EDivByZero.Create('设置零分频器除以零错误')
在ReturnAddress;
end;
Result.FSign:= Math.sign(a);
case result.FSign of
-1:begin
SetDivisorI32(Result,a);
Result.DivideFunction:= dividefixedi32; < - error E2009

我的代码有什么问题?



SSCCE



  

接口

使用Math;

type
TFastDiv = record
private
FBuffer:UInt64; //分隔符的倒数
FDivider:integer; //分隔符本身(需要使用模数等)。
FSign:TValueSign;
DivideFunction:function(const Buffer:TFastDiv; x:integer):integer;
ModFunction:function(const Buffer:TFastDiv; x:integer):integer;
public
类运算符隐式(a:整数):TFastDiv;
end;


实现

使用SysUtils;

function dividefixedi32(const Buffer:TFastDiv; x:integer):integer;前锋;

类操作符TFastDiv.Implicit(a:integer):TFastDiv;
begin
if(a = 0)then begin raise EDivByZero.Create('设置一个零分频器是一个除以零错误')在ReturnAddress;结束;
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 //缓冲区
imul dword [r9] // m
lea eax,[rdx + r8] // r8 = r8或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编译。你可以让它编译多种方式。例如,删除数据成员将导致成功编译。

 程序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] code>属性给过程类型的参数。显然,这在XE3中编译:

 程序soq19147523_version3; 

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

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

procedure foo;
var
rec:TRecord;
begin
rec.proc:= myproc; //在XE3中编译,在XE2中没有[ref]
end;

begin
end。

这给我们一个强大的线索,说明编译器在做什么。未装饰的 const 记录参数通过值或引用传递。如果记录足够小以适合寄存器,它将通过值传递。



当编译器处理记录时,它没有完全确定记录的大小。我猜在编译器内部有一个变量包含记录的大小。直到记录的声明完成,我假定这个大小变量为零。因此,编译器决定记录类型的 const 参数将通过寄存器中的值传递。当遇到过程 myproc 时,记录的真实大小是已知的。它不适合寄存器,因此编译器识别不匹配。



确实,您可以删除



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



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



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

 程序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:双;
const
零:TComplex =(R:0.0,I:0.0);
end;

无法在 Zero with E2086类型'TComplex'尚未完全定义。



另一个限制是类型A无法引用类型B,反之亦然。我们可以为类而不是记录提前声明。我理解编译器实现需要修改以支持这一点,但它当然可以实现。



还有更多。为什么不能允许继承记录?我不想要多态性,我只是想继承数据成员和方法的记录。我甚至不需要是一个行为,你得到与类。这是我不介意如果 TDerivedRecord 不是 TBaseRecord 。我只想要的是继承成员和函数,以避免重复。



很遗憾,在我看来,这是一个已经做了90%的功能,只是缺少招标,需要充分的关心才能完成。


I get the following error:

E2009 Incompatible types: 'Parameter lists differ'

However I disagree, looking at the definitions I can see no difference.

Here's the record definition:

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

And here's the Mod function I want to assign:

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

The following assignment issues the error:

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

What's wrong with my code?

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.

解决方案

First of all, some general advice. Your SSCCE is poor. It is neither short nor self-contained. This is actually rather important. Making the demonstration code as short as possible frequently helps you understand the problem. That is definitely the case here.

Here's my take on an 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.

This fails to compile with E2009. You can make it compile a number of ways. For instance, removing the data member results in successful compilation.

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.

In XE3 you can make it compile by adding the [ref] attribute to the parameter of the procedural type. To be explicit, this compiles in 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.

This gives us a strong clue as to what the compiler is doing. An undecorated const record parameter is passed either by value or by reference. If the record is small enough to fit into a register, it will be passed by value.

When the compiler is processing the record, it has not fully finalised the record's size. I'm guessing that internally in the compiler there is a variable containing the size of the record. Until the record's declaration is complete, I posit that this size variable is zero. So the compiler decides that the const parameter of record type will be passed by value in a register. When the procedure myproc is encountered, the record's true size is known. It does not fit in a register, and the compiler therefore recognises a mismatch. The type in the record receives its parameter by value, but that being offered for assignment passes the parameter by reference.

Indeed, you can remove the [ref] from the declaration of myproc and the program still compiles.

This also explains why you found that using a var parameter resulted in successful compilation. This obviously forces the parameter to be passed by reference.

If you can move to XE3 or later then the solution is obvious: use [ref] to force the compiler's hand.

If you cannot move to XE3 then perhaps an untyped const parameter is the best solution. This also forces the compiler to pass the parameter by reference.

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.


Regular readers of my postings here on Stack Overflow will know that I'm a big proponent of operator overloading on value type records. I use this feature extensively and it results in efficient and highly readable code. However, when you start pushing hard with more complex and interdependent types, the design and implementation breaks down.

The flaw highlighted in this question is one good example. It's really not uncommon to expect that the compiler could handle this. It's very reasonable to expect a type to be able to refer to itself.

Another example where the implementation lets the programmer down is when you wish to put a const of the record type in that record. For example, consider this type:

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

This fails to compile at the declaration of Zero with E2086 Type 'TComplex' is not yet completely defined.

Another limitation is the inability of type A to refer to type B, and vice versa. We can make forward declarations for classes, but not records. I understand that the compiler implementation would need to be modified to support this, but it's certainly possible to achieve.

And there's more. Why is it not possible to allow inheritance for records? I don't want polymorphism, I just want to inherit the data members and methods of the record. And I don't even need the is a behaviour that you get with classes. That is I don't mind if TDerivedRecord is not a TBaseRecord. All I want is to inherit members and functions to avoid duplication.

Sadly, to my mind, this is a feature that has been done 90% and is just missing the tender, loving care needed to take it to completion.

这篇关于E2009不兼容类型:'参数列表不同'的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆