Безопасно ли печатать TArray <X> в массив X?

Сегодня я обнаружил ошибку компилятора (QС# 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<T> литье, подсчете ссылок и т.д., чтобы быть уверенным, что это безопасно.

Есть ли кто-нибудь, кто знает достаточно, чтобы сказать так или иначе, будет ли это выдавать правильный код во время выполнения?

Ответы

Ответ 1

Внутренняя процедура компилятора SetLength строит массив измерений "на лету" в стеке и вызывает DynArraySetLength для любого динамического массива, будь то общий или нет. Если общий массив не будет структурно совместим с регулярным динамическим массивом, то такая же реализация для установки длины, возможно, не будет вызвана.

Фактически документация DynArraySetLength предлагает SetLength как альтернативу для многомерных массивов. DynArraySetLength также может использоваться вместо typecast, но я не вижу причин предпочитать тот или иной.

Ответ 2

Построив реализацию генериков, используйте ручную карту для array of array of Integer.

Но нет никакой пользы от использования дженериков здесь!

Просто код:

type
  TArrayOfArrayOfInteger = array of array of Integer;

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

Обратите внимание, что такие TArray<> или array of .. передаются по значению и копируются в стек, если вы не укажете const или var:

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);

Ответ 3

Недавно я был укушен фактом, что DynamicArray<T> и TArray<T> в С++ фактически реализованы по-разному (DynamicArray является автономным классом, тогда как TArray является потомком TObject), что означает, что array of T и TArray<T> имеют некоторые отличия в Delphi. Они, конечно же, производят разные типы RTTI. Какова была основная причина проблемы в некоторых из моего кода на С++, которые начали сбой, когда компилятор Delphi начал выводить TArray typedefs в файлах HPP для типов Delphi array of ... вместо DynamicArray typedefs.