Справочник по Ada 2012
7.3.1 Скрытые операции
Для типа, объявленного в видимой части пакета или обобщённого пакета, некоторые операции над типом становятся видимыми позднее — либо в закрытой части, либо в теле пакета. Такие скрытые операции доступны только внутри области объявления пакета или обобщённого пакета.
Статическая семантика
Предопределённые операторы, существующие для данного типа, определяются классами, к которым принадлежит тип. Например, целочисленный тип имеет предопределённый оператор "+". В большинстве случаев, предопределённые операторы типа объявляются непосредственно после определения типа; исключения объяснены ниже. Наследуемые подпрограммы также неявно объявляются сразу после определения типа, за исключением случаев, указанных ниже.
Для составного типа характеристики (см. 7.3) типа частично определяются характеристиками его компонентных типов. В месте объявления составного типа используются только те характеристики компонентных типов, которые видимы в этом месте. Если позднее, непосредственно внутри области объявления составного типа, становятся видимыми дополнительные характеристики компонентного типа, то соответствующие характеристики становятся видимыми и для составного типа. Любые дополнительные предопределённые операторы неявно объявляются в этом месте. Если такого места нет, то дополнительные предопределённые операторы не объявляются вообще, но они всё равно существуют.
Соответствующее правило применяется к типу, определённому с помощью derived_type_definition, если существует место непосредственно внутри области объявления типа, где становятся видимыми дополнительные характеристики его родительского типа.
Например, тип массива, чьим компонентным типом является ограниченный закрытый тип, становится неограниченным, если полное представление компонентного типа является неограниченным и видимым в каком-либо более позднем месте непосредственно внутри области объявления типа массива. В таком случае предопределённый оператор "=" неявно объявляется в этом месте, и присваивание разрешается после этого места.
Тип является потомком полного представления некоторого предка своего родительского типа только в том случае, если текущее представление его родителя является потомком полного представления этого предка. Более общо, в любой момент времени тип происходит от того же представления предка, что и текущее представление его родителя. Это представление определяет, какие характеристики наследуются от предка, и, например, считается ли тип потомком типа записи или потомком только через расширения записей более отдалённого предка.
Кроме того, возможно, что существуют места, где известно, что производный тип косвенно происходит от предкового типа, но не является потомком даже частичного представления предкового типа, поскольку родитель производного типа не является видимым потомком предка. В этом случае производный тип не наследует никаких характеристик от этого предка, но тем не менее находится в классе производных предка для целей преобразования типов, отношения "охватывает" и сопоставления с формальным производным типом. В этом случае производный тип фактически является потомком неполного представления предка.
Наследуемые примитивные подпрограммы следуют другому правилу. Для derived_type_definition каждая наследуемая примитивная подпрограмма неявно объявляется в самом раннем месте, если таковое имеется, непосредственно внутри области объявления, где происходит type_declaration, но после type_declaration, где соответствующее объявление из родителя является видимым. Если такого места нет, то наследуемая подпрограмма не объявляется вообще, но она всё равно существует. Для помеченного типа возможно обращение к наследуемой подпрограмме, которая вообще не объявлена.
Для private_extension_declaration каждая наследуемая подпрограмма объявляется сразу после private_extension_declaration, если соответствующее объявление из предка видимо в этом месте. В противном случае наследуемая подпрограмма не объявляется для закрытого расширения, хотя она может быть объявлена для полного типа.
Атрибут Class определён для помеченных подтипов в 3.9. Кроме того, для каждого подтипа S неопознанного закрытого типа, полное представление которого помечено, определяется следующий атрибут:
S'Class
Обозначает подтип, соответствующий полному представлению S. Этот атрибут разрешается только с начала закрытой части, в которой объявлено полное представление, до объявления полного представления. После полного представления можно использовать атрибут Class полного представления.
ПРИМЕЧАНИЯ
9 Поскольку частичное представление и полное представление являются двумя разными представлениями одного и того же типа, за пределами определяющего пакета характеристики типа определяются видимой частью. Внутри этих внешних программных модулей тип представляет собой просто закрытый тип или закрытое расширение, и любое языковое правило, которое применяется только к другому классу типов, не применяется. Тот факт, что полное объявление может реализовывать закрытый тип определённого класса (например, как тип массива), актуален только внутри области объявления самого пакета, включая любые дочерние модули.
Однако последствия этой фактической реализации действительны повсюду. Например: выполняется любая инициализация компонентов по умолчанию; атрибут Size предоставляет размер полного представления; окончание всё ещё выполняется для управляемых компонентов полного представления; правила зависимости задач всё ещё применяются к компонентам, которые являются объектами задач.
10 Частичные представления обеспечивают инициализацию, проверки на членство, выделенные компоненты для выбора дискриминантов и наследуемых компонентов, квалификацию и явное преобразование.
11 Для подтипа S частичного представления определён S'Size (см. 13.3). Для объекта A частичного представления определены атрибуты A'Size и A'Address (см. 13.3). Атрибуты Position, First_Bit и Last_Bit также определены для дискриминантов и наследуемых компонентов.
Примеры
Пример типа со скрытыми операциями:
package Менеджер_ключей is
type Ключ is private;
Нулевой_Ключ : constant Ключ; -- отложенное объявление константы (см. 7.4)
procedure Получить_Ключ(K : out Ключ);
function "<" (X, Y : Ключ) return Boolean;
private
type Ключ is new Natural;
Нулевой_Ключ : constant Ключ := Ключ'First;
end Менеджер_ключей;
type Ключ is private;
Нулевой_Ключ : constant Ключ; -- отложенное объявление константы (см. 7.4)
procedure Получить_Ключ(K : out Ключ);
function "<" (X, Y : Ключ) return Boolean;
private
type Ключ is new Natural;
Нулевой_Ключ : constant Ключ := Ключ'First;
end Менеджер_ключей;
package body Менеджер_ключей is
Последний_Ключ : Ключ := Нулевой_Ключ;
procedure Получить_Ключ(K : out Ключ) is
begin
Последний_Ключ := Последний_Ключ + 1;
K := Последний_Ключ;
end Получить_Ключ;
Последний_Ключ : Ключ := Нулевой_Ключ;
procedure Получить_Ключ(K : out Ключ) is
begin
Последний_Ключ := Последний_Ключ + 1;
K := Последний_Ключ;
end Получить_Ключ;
function "<" (X, Y : Ключ) return Boolean is
begin
return Natural(X) < Natural(Y);
end "<";
end Менеджер_ключей;
begin
return Natural(X) < Natural(Y);
end "<";
end Менеджер_ключей;
ПРИМЕЧАНИЯ
12 Примечания к примеру: За пределами пакета Менеджер_ключей доступные операции для объектов типа Ключ включают присваивание, сравнение на равенство или неравенство, процедуру Получить_Ключ и оператор "<"; они не включают другие реляционные операторы, такие как ">=", или арифметические операторы.
Явно объявленный оператор "<" скрывает предопределённый оператор "<" неявно объявленный full_type_declaration. Внутри тела функции необходимо явное преобразование X и Y в подтип Natural для вызова оператора "<" родительского типа. В качестве альтернативы, результат функции можно записать как not (X >= Y), так как оператор ">=" не переопределён.
Значение переменной Последний_Ключ, объявленной в теле пакета, остаётся неизменным между вызовами процедуры Получить_Ключ. (См. также ПРИМЕЧАНИЯ 7.2.)