항목 41 : 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성부터

  • 객체 지향 프로그래밍에서 중요한 것
    1. 명시적 인터페이스
    2. 런타임 다형성
  • 템플릿과 일반화 프로그래밍에서 중요한 것
    1. 암시적 인터페이스
    2. 컴파일 타임 다형성
  • 컴파일 타임 다형성 : 템플릿 프로그래밍을 통해 템플릿 인스턴스화가 일어날 때 함수 템플릿에 어떤 템플릿 매개변수가 들어가느냐가 컴파일 시에 결정되는 것
    • 오버로드된 함수 중 ‘지금 호출될 것을 선택(컴파일 타임) / 가상 함수 호출의 동적 바인딩(런타임)’의 차이와 흡사하다.
  • 명시적 인터페이스 : 함수 시그니처(함수 이름, 매개변수 타입, 반환 타입 등) 기반
  • 암시적 인터페이스 : 유효 표현식(템프릿 매개변수에 대해 걸리는 제약)
  • ex)
    template<typename T> 
    void doProcessing(T& w) 
    { 
      if(w.size() > 10) // 정수 계열 값을 반환하는 이름이 size인 함수가 있어야 한다. 
      .... 
    }
    

항목 42 : typename의 두 가지 의미를 제대로 파악하자

  • 의존 이름(dependent name) : 템플릿 매개변수에 종속적이다.(ex)매개변수 C에 따라 바뀜)
  • 중첩 의존 이름(nested dependent name) : 의존 이름이 어떤 클래스 안에 중첩된다.(ex)C::const_iterator)
  • 비의존 이름(non-dependent name) : 템플릿 매개변수와 관계 없는 타입 이름(ex)int)

  • C++ 모호성 규칙 : 구문 분석기는 템플릿 안에서 중첩 의존 이름을 만나면 프로그래머가 타입이라고 알려주지 않는 한 그 이름이 타입이 아닌 것으로 해석한다.
  • ex)
    C::const_iterator iter(container.begin()); 
    // C::const_iterator를 타입이 아닌 것으로 가정한다.(template<typename C>) 
    
  • typename 키워드 사용 시 타입으로 지정이 가능하다.
  • ex)
    typename C::const_iterator iter(container.begin()); 
    
  • typename 키워드는 중첩 의존 이름만 식별하는 데 써야 한다.
  • ex)
    template<typename C>              // typename을 쓸 수 있다. 
    void f(const C& container,          // typename 쓰면 안된다. 
          typename C::iterator iter);  // typename 써야 한다. 
    
  • 예외 : 중첩 의존 타입 이름이 기본 클래스의 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있을 시에는 typename을 붙이면 안된다.
  • ex)
    template<typename T> 
    class Derived : public Base<T>::Nested          // 상속되는 기본 클래스 리스트. typename 사용 안됨 
    { 
    public: 
      explicit Derived(int x) : Base<T>::Nested    // 멤버 초기화 리스트에 있는 기본 클래스 식별자. typename사용 안됨 
      { 
          typename Base<T>::Nested temp;  // 중첩 의존 타입, 기본 클래스 리스트도 멤버 초기화 리스트도 아니므로 typename 필요 
      } 
    }; 
    

항목 43 : 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아두자

  • 일반적으로 상속받아 기본 클래스의 이름에 접근하려 하면 컴파일 에러가 발생한다.
    • 템플릿 매개변수가 나중까지 무엇이 될지 알 수 없기 때문이다.
  • 완전 템플릿 특수화 : 이 템플릿은 특정 타입에 대해 특수화 되었고 템플릿의 매개변수들이 하나도 빠짐없이 구체적인 타입으로 정해진 상태이다.
  • 특수화 버전에는 해당 함수가 없을 수 있으므로 컴파일 에러가 난다.
    • 기본 클래스 템플릿은 언제라도 특수화 될 수 있고, 특수화 버전 인터페이스는 원래의 일반형 템플릿과 꼭 같으리란 보장이 없다.
  • 객체 지향 C++ -> 템플릿 C++ 시 상속 매커니즘이 끊긴다.

  • 템플릿에서 기본 클래스 사용 방법
    1. 기본 클래스 함수에 대한 호출문 앞에 ‘this->’를 추가한다.
    2. using 선언을 한다.
    3. 호출 함수가 기본 클래스 함수라는 것을 명시적으로 지정한다.
      • ex) MsgSender::send >>+ 호출 함수가 가상 함수일 경우 가상 함수 바인딩이 무시되므로 추천되지 않는 방법이다.
  • 기존 클래스 템플릿이 어떻게 특수화 되더라도 원래의 일반형 템플릿에서 제공하는 인터페이스를 그대로 제공할 것이라고 컴파일러에게 약속하는 것
  • 특수화에서 인터페이스를 제공하지 않으면 컴파일 에러가 발생한다.

  • 기본 클래스 멤버에 대한 참조가 무효한지 컴파일러가 진단하는 과정의 위치
    1. 파생 클래스 템플릿의 정의가 구문분석 될 때(이른 진단)(지금 항목 상황일 때)
    2. 파생 클래스 템플릿이 특정한 템플릿 매개변수를 받아 인스턴스화 될 때(기본)