제네릭 타입 특수화(Generic Type Specialization)

제네릭 타입 특수화(Generic Type Specialization)

제네릭을 정의해만 두면 쓸 수 없어요. 자리표시자를 실제 타입으로 채워서 **특수화(specialize)**해야 비로소 쓸 수 있는 타입이 돼요. 이 과정은 마치 저장해 둔 정의를 재생하면서 템플릿 자리표시자를 실제 타입 정의로 채우는 것과 같아요.

출처: Generic type specialization

본문

일단 제네릭 타입이 정의되면, 그것을 이용해 다른 타입을 만들어 낼 수 있어요. 템플릿 자리표시자를 실제 타입 정의로 채워서 타입 정의를 '재생'하는 것과 같아요. 이 작업은 어떤 type 정의 블록에서든 할 수 있어요.

특수화된 타입은 아주 단순한 정의예요.

Specialized type

앞 절에서 선언한 TList를 기준으로, 아래는 유효한 타입 정의예요.

Type
   TPointerList = specialize TList<Pointer>;
   TIntegerList = specialize TList<Integer>;

Free Pascal 3.0 버전부터는 specialize 키워드를 변수 선언에서도 쓸 수 있어요.

Var
   P : specialize TList<Pointer>;

specialize 키워드는 특수화된 타입의 일부예요. 그래서 완전히 정규화된 이름(full qualified name)을 쓸 때에는 specialize 키워드가 유닛 이름과 부모 타입 이름 뒤에 와야 해요. specialize 문 안의 타입은 알려져 있어야 해요. 다만 다른 제네릭 타입 정의 안에서는 예외예요.

두 제네릭 클래스 정의를 볼게요.

type
   Generic TMyFirstType<T1> = Class(TMyObject);
   Generic TMySecondType<T2> = Class(TMyOtherObject);

그렇다면 아래 특수화는 유효하지 않아요.

type
   TMySpecialType = specialize TMySecondType<TMyFirstType>;

이유는 TMyFirstType이 제네릭 타입이라 완전히 정의되지 않았기 때문이에요. 컴파일러는 이렇게 불평해요.

Error: Generics cannot be used as parameters when specializing generics

하지만 아래는 허용돼요.

type
   TA = specialize TMyFirstType<Atype>;
   TB = specialize TMySecondType<TA>;

TB를 특수화할 때 TA는 이미 완전히 정의돼 있으니까요.

또한 specialize 키워드는 다른 제네릭 타입 정의 안에서도 쓸 수 있어요. 위 예시에서 본 것처럼요.

generic TList<_T>=class(TObject, specialize IList<_T>)

그리고

generic TPointSet<t> = array of specialize PlanarCoordinate<t>;

이런 정의에서 특수화는 제네릭 타입 자신이 특수화될 때만 수행되고, 그 시점에는 타입들이 알려져 있어요.

주석: 제네릭의 특수화인 클래스의 전방 정의(forward definition)는 만들 수 없어요. 즉 아래 코드는 컴파일되지 않아요.

TMyClass = Class;

   // Other declarations

   TMyClass = specialize TList<T>;

더 알아보기

  • 제네릭 타입을 정의하는 방법은 8.2 절에서 다뤄요.
  • 제네릭에 제약을 거는 방법은 8.4 절에서 확인할 수 있어요.
  • specialize가 어떤 의미인지는 8.1 절(제네릭 소개)을 참고해요.