E2009不兼容类型:'参数列表不同' [英] E2009 Incompatible types: 'Parameter lists differ'
问题描述
我收到以下错误:
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 proceduremyproc
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 ofmyproc
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 aTBaseRecord
. 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屋!