我试图弄清楚是否可以使用Delphi(Berlin 10.1 upd 1)中的“implicit”类运算符初始化包含动态数组的记录,
附加的程序产生以下输出:
ci iA r1 r2 r3 1 1 1 1 49694764491115752 2 2 2 2 11570520 3 3 3 3 0 4 4 4 4 0 5 5 5 5 0
> TRec是包含要初始化的动态数组的记录类型。
> ci是整数的常数数组。
ia是整数的动态数组。
> r1,r2,r3是以不同方式初始化的TREC类型的记录。
从输出可以看出,前两个赋值(r1,r2),使用常数按预期工作。第三个赋值r3:= iArray被编译器接受,但结果被破坏。调试器显示在TRec.Implicit中v的值已经错误了。
这里出了什么问题?这是可能的吗
program Project5; {$APPTYPE CONSOLE} {$R *.res} type TRec = record iArray: array of UInt64; class operator Implicit(const v: array of UInt64): TRec; end; { TRec } class operator TRec.Implicit(const v: array of UInt64): TRec; var i: integer; begin setlength(Result.iArray,Length(v)); for i := 0 to High(v) do Result.iArray[i] := v[i]; end; const ciArray: array [0 .. 4] of UInt64 = (1,2,3,4,5); var i : integer; iArray : array of UInt64; r1,r2,r3: TRec; begin iArray := [1,5]; r1 := [1,5]; r2 := ciArray; r3 := iArray; Writeln('ci iA r1 r1 r3'); for I := 0 to High(ciArray) do Writeln(ciArray[i],' ',iArray[i],r1.iArray[i],r2.iArray[i],r3.iArray[i]); readln; end.
解决方法
看起来你发现了一个bug代码在那里(它也存在于Win64编译器)。我查看了生成的asm,似乎编译器会为运算符重载产生错误的指令。这就是为什么错误的值最终在操作符重载的数组中。请在质量门户中报告。
Project109.dpr.46: r3 := iArray; 0040B1F2 A1FC044100 mov eax,[$004104fc] 0040B1F7 8945E8 mov [ebp-$18],eax 0040B1FA 837DE800 cmp dword ptr [ebp-$18],$00 0040B1FE 740B jz $0040b20b 0040B200 8B45E8 mov eax,[ebp-$18] 0040B203 83E804 sub eax,$04 0040B206 8B00 mov eax,[eax] 0040B208 8945E8 mov [ebp-$18],eax 0040B20B 8D4DD8 lea ecx,[ebp-$28] 0040B20E 8B55E8 mov edx,[ebp-$18] 0040B211 4A dec edx 0040B212 B8FC044100 mov eax,$004104fc // <-- wrong one 0040B217 E87CF5FFFF call TRec.&op_Implicit
Project109.dpr.47: r3 := TRec.Implicit(iArray); 0040B22F A1FC044100 mov eax,[$004104fc] 0040B234 8945E4 mov [ebp-$1c],eax 0040B237 837DE400 cmp dword ptr [ebp-$1c],$00 0040B23B 740B jz $0040b248 0040B23D 8B45E4 mov eax,[ebp-$1c] 0040B240 83E804 sub eax,$04 0040B243 8B00 mov eax,[eax] 0040B245 8945E4 mov [ebp-$1c],eax 0040B248 8D4DD4 lea ecx,[ebp-$2c] 0040B24B 8B55E4 mov edx,[ebp-$1c] 0040B24E 4A dec edx 0040B24F A1FC044100 mov eax,[$004104fc] // <-- correct one 0040B254 E8CFF5FFFF call TRec.Implicit
但是,您可以通过为参数类型为TArray< UInt64>的Implicit操作符添加另一个重载来避免这种情况。然后还声明您的本地变量为该类型,以便编译器选择正确的重载(在这种情况下它不会生成错误的代码)。
但请注意,这将仅在您传递类型为TArray< UInt64>并且由于Delphis严格类型规则,当您有任何其他UInt64的动态数组时,调用错误的一个。