Справочник по Ada 2005
3.9.2 Диспетчеризация операций помеченных типов
Примитивные подпрограммы помеченного типа, подпрограммы, объявленные с помощью formal_abstract_subprogram_declaration, и потоковые атрибуты конкретного помеченного типа, доступные (см. 13.13.2) в конце списка объявления типа, называются операциями диспетчеризации. Операцию диспетчеризации можно вызвать, используя статически определенную управляющую метку, в этом случае тело, подлежащее выполнению, определяется во время компиляции. В качестве альтернативы, управляющая метка может быть определена динамически, в этом случае вызов диспетчеризует выполнение к телу, которое определяется во время выполнения; такой вызов называется диспетчеризованным вызовом. Как описано ниже, свойства операндов и контекст конкретного вызова операции диспетчеризации определяют, как определяется управляющая метка, и, следовательно, является ли вызов диспетчеризованным. Полиморфизм во время выполнения достигается, когда операция диспетчеризации вызывается диспетчеризованным вызовом.
Статическая семантика
Вызов операции диспетчеризации — это вызов, имя или префикс которого обозначает объявление операции диспетчеризации. Управляющий операнд в вызове операции диспетчеризации помеченного типа T — это операнд, соответствующий формальному параметру типа T или анонимного типа доступа с целевым типом T; соответствующий формальный параметр называется управляющим формальным параметром. Если управляющий формальный параметр является параметром доступа, управляющим операндом является объект, на который ссылается фактический параметр, а не сам фактический параметр. Если вызов относится к (примитивной) функции с типом результата T, то вызов имеет управляющий результат — контекст вызова может управлять диспетчеризацией. Аналогично, если вызов относится к функции с типом результата доступа, обозначающим T, то вызов имеет управляющий результат доступа, и контекст аналогичным образом может управлять диспетчеризацией.
Имя или выражение помеченного типа являются либо статически помеченными, динамически помеченными, либо не определенными по метке, в зависимости от того, когда они используются в качестве управляющего операнда, метка, которая управляет диспетчеризацией, определяется статически типом операнда (конкретным), динамически его меткой во время выполнения или контекстом. Квалифицированное выражение или выражение в скобках статически, динамически или неопределённо помечены в зависимости от своего операнда. Для других типов имён и выражений это определяется следующим образом:
- Имя или выражение статически помечены, если они имеют конкретный помеченный тип и, если это вызов с управляющим результатом или управляющим результатом доступа, у него есть хотя бы один статически помеченный управляющий операнд;
- Имя или выражение динамически помечены, если они имеют тип класса, или это вызов с управляющим результатом или управляющим результатом доступа, и у него есть по крайней мере один динамически помеченный управляющий операнд;
- Имя или выражение не определены по метке, если это вызов с управляющим результатом или управляющим результатом доступа, все управляющие операнды (если таковые имеются) которых не определены по метке.
Преобразование типа type_conversion статически или динамически помечено в зависимости от того, является ли тип, определяемый subtype_mark, конкретным или относящимся к классу, соответственно. Для объекта, который обозначается выражением, ожидаемым типом которого является анонимный тип доступа к конкретному помеченному типу, объект динамически помечен, если выражение, игнорируя окружающие скобки, имеет вид X'Access, где X имеет тип класса, или имеет вид new T'(...), где T обозначает подтип класса. В противном случае объект статически или динамически помечен в зависимости от того, является ли целевой тип выражения конкретным или относящимся к классу, соответственно.
Правила легальности
Вызов операции диспетчеризации не должен иметь как динамически, так и статически помеченные управляющие операнды.
Если ожидаемый тип для выражения или имени является каким-то конкретным помеченным типом, то выражение или имя не должно быть динамически помеченным, если это не управляющий операнд в вызове операции диспетчеризации. Аналогично, если ожидаемый тип для выражения является анонимным типом доступа к конкретному помеченному типу, то объект, на который указывает выражение, не должен быть динамически помечен, если это не управляющий операнд в вызове операции диспетчеризации.
В объявлении операции диспетчеризации помеченного типа, где подтип помеченного типа появляется как подтип профиля (см. 6.1), он должен статически соответствовать первому подтипу помеченного типа. Если операция диспетчеризации переопределяет унаследованную подпрограмму, она должна быть совместима с подтипом унаследованной подпрограммы. Конвенция унаследованной операции диспетчеризации — это конвенция соответствующей примитивной операции родительского или предкового типа. По умолчанию конвенция операции диспетчеризации, которая переопределяет унаследованную примитивную операцию, — это конвенция унаследованной операции; если операция переопределяет несколько унаследованных операций, то все они должны иметь одинаковую конвенцию. Явно объявленная операция диспетчеризации не может иметь конвенцию Intrinsic.
Выражение по умолчанию для управляющего формального параметра операции диспетчеризации должно быть неопределённым по метке.
Если операция диспетчеризации определена с помощью subprogram_renaming_declaration или с помощью инстанцирования обобщённой подпрограммы, любой параметр доступа переименованной подпрограммы или обобщённой подпрограммы, соответствующий управляющему параметру доступа операции диспетчеризации, должен иметь подтип, исключающий null.
Данная подпрограмма не может быть операцией диспетчеризации двух или более различных помеченных типов.
Явное объявление примитивной подпрограммы помеченного типа должно произойти до того, как тип заморожен (см. 13.14). Например, новые операции диспетчеризации не могут быть добавлены после существования объектов или значений типа, ни после получения расширения записи из него, ни после тела.
Динамическая семантика
Для выполнения вызова операции диспетчеризации типа T, значение управляющей метки определяет, какое тело подпрограммы будет выполнено. Значение управляющей метки определяется следующим образом:
- Если один или несколько управляющих операндов статически помечены, то значение управляющей метки статически определяется как метка T.
- Если один или несколько управляющих операндов динамически помечены, то значение управляющей метки не определяется статически, а определяется метками управляющих операндов. Если имеется более одного динамически помеченного управляющего операнда, выполняется проверка, что все они имеют одинаковую метку. Если эта проверка завершается неудачно, генерируется ошибка Constraint_Error, за исключением случаев, когда вызов является вызовом функции function_call, имя которого обозначает объявление оператора равенства (предопределённого или пользователя), который возвращает Boolean, в этом случае результат вызова определён таким образом, чтобы указывать на неравенство, и ни одно тело подпрограммы subprogram_body не выполняется. Эта проверка выполняется до оценки любых управляющих операндов, неопределённых по метке.
- Если все управляющие операнды (если таковые имеются) не определены по метке, то:
- Если вызов имеет управляющий результат или управляющий результат доступа и сам является (возможно, заключенным в скобки или квалифицированным) управляющим операндом вложенного вызова операции диспетчеризации потомка типа T, то его управляющее значение метки определяется управляющим значением метки этого вложенного вызова;
- Если вызов имеет управляющий результат или управляющий результат доступа и (возможно, заключенный в скобки, квалифицированный или ссылающийся) является выражением оператора присваивания assignment_statement, целевой объект которого имеет тип класса, то его управляющее значение метки определяется целевым объектом;
- В противном случае управляющее значение метки определяется статически как метка типа T.
При выполнении вызова операции диспетчеризации выполняемое действие определяется свойствами соответствующей операции диспетчеризации конкретного типа, определённого значением управляющей метки. Если соответствующая операция явно объявлена для этого типа, даже если объявление находится в частях, которые объявлены скрытыми, то действие включает вызов явного тела операции. Если соответствующая операция неявно объявлена для этого типа:
- если операция реализуется с помощью входа или подпрограммы-защитника (см. 9.1 и 9.4), то действие включает вызов этого входа или подпрограммы-защитника, причём целевой объект задаётся первым фактическим параметром вызова, а фактические параметры подпрограммы-входа или подпрограммы-защитника задаются остальными фактическими параметрами вызова, если таковые имеются;
- в противном случае действие такое же, как действие для соответствующей операции родительского типа.
ПРИМЕЧАНИЯ
72 Тело, выполняемое при вызове операции диспетчеризации, определяется тегом; не имеет значения, определяется ли этот тег статически или динамически, и не имеет значения, видна ли декларация подпрограммы в месте вызова.
73 Этот подпункт охватывает вызовы диспетчеризованных подпрограмм помеченного типа. Правила для тестов на принадлежность к помеченному типу описаны в 4.5.2. Управление определением тега для выражения_присваивания описано в 5.2.
74 Вызов диспетчеризации может выполнить диспетчеризацию к телу, чья декларация не видна в месте вызова.
75 Вызов через значение доступа к подпрограмме никогда не является вызовом диспетчеризации, даже если значение доступа указывает на операцию диспетчеризации. Аналогично, вызов, чьё префикс обозначает декларацию_переименования_подпрограммы, не может быть вызовом диспетчеризации, если переименование само по себе не является декларацией примитивной подпрограммы.