Проверка типа и проверка диапазона
В одном из учебников, которые я читаю, говорится: "Хотя типы проверяются на совместимость во время компиляции, поддиапазоны требуют проверки диапазона времени выполнения". Если я правильно понял, проверка во время выполнения должна выполняться, когда новые значения назначаются переменной, определенной в определенном поддиапазоне (подтипе). Разве это не то же самое для типов? Почему эта разница? Это просто по умолчанию компилятор?
2 ответа
Это правда, что присвоение из подтипа P
типа U
не требует проверок, поскольку не может быть несоответствия (даже если подтип имеет тот же диапазон, что и тип; subtype P is U;
совершенно законно и может быть полезным!)
Другой путь, от типа к подтипу, будет включать проверки, по крайней мере, если подтип является поддиапазоном.
Снаружи то же самое относится и к другому типу с другим диапазоном. Тем не менее, есть небольшая разница; с типом / подтипом вы можете назначить одно другому без преобразования, если не без проверок, но с типом / типом вы должны преобразовать тип перед назначением, и любое преобразование нарушения будет проверяться при преобразовании, а не назначении.
Это может помочь проиллюстрировать это:
procedure Drimades is
type Upper is range 10 .. 20;
subtype Part is Upper range 10 .. 15;
type Lower is new Upper range 10 .. 15;
procedure Assign_U_To_P (U : Upper; To : out Part) is
begin
To := U; -- requires check
end Assign_U_To_P;
procedure Assign_P_To_U (P : Part; To : out Upper) is
begin
To := P; -- no check needed
end Assign_P_To_U;
procedure Assign_U_To_L (U : Upper; To : out Lower) is
begin
To := Lower (U); -- requires check
end Assign_U_To_L;
procedure Assign_L_To_U (L : Lower; To : out Upper) is
begin
To := Upper (L); -- no check required
end Assign_L_To_U;
U : Upper;
P : Part;
L : Lower;
begin
Assign_U_To_P (20, P);
Assign_P_To_U (15, U);
Assign_U_To_L (20, L);
Assign_L_To_U (15, U);
end Drimades;
Компиляция этого с GNAT и коммутатором -gnatG
выдает это промежуточное представление, не слишком сложное для интерпретации, я надеюсь:
procedure drimades is
type drimades__upper is range 10 .. 20;
[type drimades__TupperB is new short_short_integer]
freeze drimades__TupperB []
subtype drimades__part is drimades__upper range 10 .. 15;
[type drimades__TlowerB is new drimades__TupperB]
freeze drimades__TlowerB []
type drimades__lower is new drimades__upper range 10 .. 15;
procedure drimades__assign_u_to_p (u : drimades__upper; to : out
drimades__part) is
begin
[constraint_error when
not (u in 10 .. 15)
"range check failed"]
to := u;
return;
end drimades__assign_u_to_p;
procedure drimades__assign_p_to_u (p : drimades__part; to : out
drimades__upper) is
begin
to := p;
return;
end drimades__assign_p_to_u;
procedure drimades__assign_u_to_l (u : drimades__upper; to : out
drimades__lower) is
begin
[constraint_error when
not (u in 10 .. 15)
"range check failed"]
to := drimades__lower(u);
return;
end drimades__assign_u_to_l;
procedure drimades__assign_l_to_u (l : drimades__lower; to : out
drimades__upper) is
begin
to := drimades__upper(l);
return;
end drimades__assign_l_to_u;
u : drimades__upper;
p : drimades__part;
l : drimades__lower;
begin
drimades__assign_u_to_p (20, p);
drimades__assign_p_to_u (15, u);
drimades__assign_u_to_l (20, l);
drimades__assign_l_to_u (15, u);
return;
end drimades;
Ответ Саймона Райта касается вопроса с точки зрения Ada и его подтипа, который означает подмножество значений в типе, а не то, что Ada или C++ называют производным типом или производным классом, соответственно. (тип Lower
новый Upper
... ;
марки Lower
быть полученным из Upper
"…", определяющий дальнейшие характеристики подтипа, созданного затем.)
В целом, некоторые языки позволяют любому имени переменной ссылаться на любой тип объекта во время выполнения. Таким образом, в JavaScript,
var a = 15, b = -1;
...
a = {"foo": "bar"}
...
return a + b;
вернет результат, который может быть удивительным, но это хорошо, если судить по языку. a
присваивается новое значение другого типа во второй строке, и +
будет производить что-то из a
а также b
на третьем.
Такая гибкость не требуется в таких языках, как Ada, C или Swift: в этом случае программа, написанная на таком языке и предназначенная для запуска на любом компьютерном устройстве, предназначена для того, чтобы не помещать объект, подобный словарю, в какое-либо место. скажем, имеет некоторый целочисленный тип, скажем. Проверка типа времени компиляции предотвращает эту ситуацию. Любое "неуважение" при назначении (или передаче) объектов должно быть обнаружено во время компиляции.
Ада, в дополнение к проверке типов времени компиляции, использует эквивалентность типов на основе имени. Так,
type Apples is range 0 .. 20;
type Oranges is range 0 .. 20;
a : Apples := 5;
b : Oranges := 8;
return a + b; -- Error!
Ты получаешь
8. return a + b; -- Error!
|
>>> invalid operand types for operator "+"
>>> left operand has type "Apples" defined at line 2
>>> right operand has type "Oranges" defined at line 3
Это чистая проверка типов во время компиляции.
Наконец, расширяя то, что иллюстрирует пример Саймона Райта, иногда Ада даже требует проверки подтипов во время компиляции. Фраза включает в себя то, что подтипы статически совпадают, где статический означает время компиляции. Например, те же границы. Но это сложная вещь, возникающая, например, когда указатели должны указывать на объекты в стеке, и они имеют диапазон (подтип), отличный от указателя.