【问题标题】:Is it safe to type-cast TArray<X> to array of X?将 TArray<X> 类型转换为 X 数组是否安全?
【发布时间】:2012-09-07 15:00:48
【问题描述】:

今天我发现了一个编译器错误 (QC#108577)。

以下程序编译失败:

program Project1;
{$APPTYPE CONSOLE}

procedure P(M: TArray<TArray<Integer>>);
begin
  SetLength(M, 1, 2);
end;

begin
end.

编译器在SetLength 行上作呕并说:

[dcc32 Error] E2029 ')' expected but ',' found

我知道我可以这样修复它:

procedure P(M: TArray<TArray<Integer>>);
var
  i: Integer;
begin
  SetLength(M, 1);
  for i := low(M) to high(M) do
    SetLength(M[i], 2);
end;

但我自然很想避免不得不诉诸于此。

以下变体编译并且似乎可以工作:

procedure P(M: TArray<TArray<Integer>>);
type
  TArrayOfArrayOfInteger = array of array of Integer;
begin
  SetLength(TArrayOfArrayOfInteger(M), 1, 2);
end;

我对动态数组、TArray&lt;T&gt; 强制转换、引用计数等的实现细节了解得不够多,无法确信这是安全的。

有没有人足够了解以一种或另一种方式说出这是否会在运行时产生正确的代码?

【问题讨论】:

  • System.pas 定义了TArray&lt;T&gt; = array of T;,所以我希望硬演员应该起作用。
  • 任何一个都以 'DynArraySetLength' 结尾(至少一个具有一维数组),所以我同意上述观点..
  • @afrazier System.pas 是特殊的,对于TArray&lt;T&gt; 可能有一些特殊的编译器魔术内在处理。这就是我所关心的。自然,他们不太可能实现一个新的不兼容的数组代码,而不是使用他们已经拥有的久经考验的代码。
  • @SertacAkyuz 这是非常有力的证据。您能否将其扩展为答案?
  • XE4 中修复了该错误。

标签: delphi generics casting delphi-xe2 dynamic-arrays


【解决方案1】:

编译器内部过程SetLength 在堆栈上动态构造一个维度数组,并为任何动态数组调用DynArraySetLength,无论它是否是通用的。如果泛型数组在结构上与常规动态数组不兼容,则可能不会调用用于设置长度的相同实现。

事实上,documentationDynArraySetLength 提供了 SetLength 作为多维数组的替代方案。 DynArraySetLength 也可以用来代替类型转换,但我看不出有任何理由偏爱其中一个。

【讨论】:

  • @David - 不客气!无论如何,我还是希望有人提出更具体的说法。
【解决方案2】:

根据泛型实现的设计,使用手动映射到array of array of Integer 将起作用。

但是在这里使用泛型没有任何好处!

代码:

type
  TArrayOfArrayOfInteger = array of array of Integer;

procedure P(M: TArrayOfArrayOfInteger);
begin
  SetLength(TArrayOfArrayOfInteger, 1, 2);
end;

另请注意,此类TArray&lt;&gt;array of .. 是按值传递的,并复制到堆栈上,除非您指定constvar

procedure P(var M: TArrayOfArrayOfInteger);
begin
  SetLength(TArrayOfArrayOfInteger, 1, 2);
end; // now caller instance of the parameter will be resized

var A: TArrayOfArrayOfInteger;
...
A := nil;
P(A);
assert(length(A)=1);
assert(length(A[0])=2);

【讨论】:

  • 你有任何文件来支持“设计”断言吗?在这里使用泛型有一个好处。在通用容器类中,我可以编写如下函数:function ToArray: TArray&lt;T&gt;。显然,如果不使用通用 TArray&lt;T&gt;,我就无法做到这一点。我对 var 和 const 了如指掌。我的例子显然是无用和毫无意义的。它被设计为说明该错误的最小可能示例。鉴于代码无法运行,担心运行时性能似乎毫无意义。
  • @David 使用泛型你可以声明一个像 function ToArray: array of T 这样的函数,并且使用嵌套类型甚至可以让你命名它。
  • @Arioch 不,该代码无法编译。无需命名 TArray。它已经有了名字。一个名字就够了。
  • @David,是的,它已经有一个名称,名称是“T 数组”;-) 我不明白为什么您要保护声明数组的两种方式的隔离。尤其是对于这种隔离仅得到 70 秒“帕斯卡报告”的支持,该报告已经颠覆了大多数其他类型,如集合和范围。
  • @Arioch 这就是你的名字。使用该名称意味着您的代码将无法编译。很没用的想法。你提出的改变是明智的,但我认为在 Emba 实施之前地狱会冻结。我可能错了,但我判断少数 RTL 更改的可能性更大。
【解决方案3】:

我最近被 C++ 中的 DynamicArray&lt;T&gt;TArray&lt;T&gt; 实际上实现不同的事实所困扰(DynamicArray 是一个独立的类,而 TArrayTObject 的后代),这意味着 @987654326 @ 和 TArray&lt;T&gt; 在 Delphi 中也有一些实现差异。至少,他们肯定会产生不同类型的 RTTI。当 Delphi 编译器开始在 Delphi array of ... 类型而不是 DynamicArray typedefs 的 HPP 文件中输出 TArray typedefs 时,我的一些 C++ 代码开始出现问题,这是导致问题的根本原因。

【讨论】:

  • 我根本不懂 Emba 的 C++。 Emba C++ 中的 DynamicArray&lt;T&gt;TArray&lt;T&gt; 是什么?它们与 Delphi 有什么关系?
  • 它们是直接对应于 Delphi 的 array of ...TArray 类型的模板类,很像 AnsiStringUnicodeStringVariant 类直接对应于它们的 Delphi 对应物。它们都是允许 C++ 和 Delphi 相互交换兼容数据的 C++ 类。
  • 然后是 EMB 的另一个案例,即“左手不知道右手的行为”。根据定义,在delphi type TArray = array of T 中。它不是像 TList 这样的类。 C++ 再次与 Delphi 不同。
  • 但是TArray&lt;T&gt; 是通用的,所以我相信这也会影响问题。我猜TArray&lt;Byte&gt;array of Byte 实际上因此而不同。他们确实解释了为什么他们有不同的 RTTI。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
相关资源
最近更新 更多