Безопасно ли печатать 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.