Проверка типа и проверка диапазона

В одном из учебников, которые я читаю, говорится: "Хотя типы проверяются на совместимость во время компиляции, поддиапазоны требуют проверки диапазона времени выполнения". Если я правильно понял, проверка во время выполнения должна выполняться, когда новые значения назначаются переменной, определенной в определенном поддиапазоне (подтипе). Разве это не то же самое для типов? Почему эта разница? Это просто по умолчанию компилятор?

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

Это чистая проверка типов во время компиляции.

Наконец, расширяя то, что иллюстрирует пример Саймона Райта, иногда Ада даже требует проверки подтипов во время компиляции. Фраза включает в себя то, что подтипы статически совпадают, где статический означает время компиляции. Например, те же границы. Но это сложная вещь, возникающая, например, когда указатели должны указывать на объекты в стеке, и они имеют диапазон (подтип), отличный от указателя.

Другие вопросы по тегам