8.8 Несколько слов об области действия

Вверх  Предыдущий  Следующий

Следует подчеркнуть, что при объявляется дженерика  классов все идентификаторы, за исключением заполнителей шаблона, должны быть известны. Это работает в двух случаях. В первом случае, все типы должны быть известны, то есть, идентификатор типа с тем же именем должен существовать. Следующий код выдаст ошибку:

{$mode objfpc}

unit myunit;

 

interface

 

type

   Generic TMyClass<T> = Class(TObject)

     Procedure DoSomething(A : T; B : TSomeType);

   end;

 

Type

   TSomeType = Integer;

   TSomeTypeClass = specialize TMyClass<TSomeType>;

 

Implementation

 

Procedure TMyClass.DoSomething(A : T; B : TSomeType);

begin

   // Какой-то код

end;

 

end.

Приведенный выше код приведет к ошибке, так как тип TSomeType не известен, когда анализируется объявление:

home: >fpc myunit.pp

myunit.pp(8,47) Error: Identifier not found "TSomeType"

myunit.pp(11,1) Fatal: There were 1 errors compiling module, stopping

 

home: >fpc myunit.pp

myunit.pp(8,47) Ошибка: Не найден идентификатор "TSomeType"

myunit.pp(11,1) Неутранимая (ошибка): Была 1 ошибка при компиляции модуля, остановка

Второй случай, когда это видно, состоит в следующем. Пример модуля:

{$mode objfpc}

unit mya;

 

interface

 

type

   Generic TMyClass<T> = Class(TObject)

     Procedure DoSomething(A : T);

   end;

 

Implementation

 

Procedure DoLocalThings;

begin

   Writeln(’mya.DoLocalThings’);

end;

 

Procedure TMyClass.DoSomething(A : T);

begin

   DoLocalThings;

end;

 

end.

Компилятор не позволит собрать этот модуль, так как процедура DoLocalThings не видна, когда тип дженерика специализируется:

Error: Global Generic template references static symtable

Ошибка: Глобальный дженерик содержит модель со ссылкой на статическую таблицу символов

Если модуль изменить так, что описание процедуры DoLocalThings переместиться в секцию интерфейса, модуль будет компилироваться. При использовании этого дженерика в программе:

{$mode objfpc}

program myb;

 

uses mya;

 

procedure DoLocalThings;

begin

   Writeln('myb.DoLocalThings');

end;

 

Type

   TB = specialize TMyClass<Integer>;

 

Var

   B : TB;

 

begin

   B:=TB.Create;

   B.DoSomething(1);

end.

Несмотря на то, что дженерики действуют как макрос, который обрабатывается во время специализации, ссылка на DoLocalThings будет учитываться, когда определяется TMyClass, а не тогда, когда определяется TB. Это объясняет, результаты работы компилятора:

home: >fpc -S2 myb.pp

home: >myb

mya.DoLocalThings

Такое поведение продиктовано соображениями безопасности и необходимостью:

1.Программист, специализирующий класс, не знает, какие локальные процедуры будут использованы, поэтому он не может случайно "переопределить" их.

2.Программист, специализирующий класс, не знает какие локальные процедуры будут использованы, поэтому он не может выполнить (реализовать) их, так как он ещё не знает параметров.

3.Если реализуемые процедуры используются как в приведенном выше примере, то они не могут ссылаться наружу модуля. Они должны быть в другом модуле целиком, и программист не имеет возможности узнать, что он должен включать их до специализации его класса.