[Effective C++] 항목 41 ~ 43
항목 41 : 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성부터
- 객체 지향 프로그래밍에서 중요한 것
- 명시적 인터페이스
- 런타임 다형성
- 템플릿과 일반화 프로그래밍에서 중요한 것
- 암시적 인터페이스
- 컴파일 타임 다형성
- 컴파일 타임 다형성 : 템플릿 프로그래밍을 통해 템플릿 인스턴스화가 일어날 때 함수 템플릿에 어떤 템플릿 매개변수가 들어가느냐가 컴파일 시에 결정되는 것
- 오버로드된 함수 중 ‘지금 호출될 것을 선택(컴파일 타임) / 가상 함수 호출의 동적 바인딩(런타임)’의 차이와 흡사하다.
- 명시적 인터페이스 : 함수 시그니처(함수 이름, 매개변수 타입, 반환 타입 등) 기반
- 암시적 인터페이스 : 유효 표현식(템프릿 매개변수에 대해 걸리는 제약)
- 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++ 시 상속 매커니즘이 끊긴다.
- 템플릿에서 기본 클래스 사용 방법
- 기본 클래스 함수에 대한 호출문 앞에 ‘this->’를 추가한다.
- using 선언을 한다.
- 호출 함수가 기본 클래스 함수라는 것을 명시적으로 지정한다.
- ex) MsgSender
::send >>+ 호출 함수가 가상 함수일 경우 가상 함수 바인딩이 무시되므로 추천되지 않는 방법이다.
- ex) MsgSender
- 기존 클래스 템플릿이 어떻게 특수화 되더라도 원래의 일반형 템플릿에서 제공하는 인터페이스를 그대로 제공할 것이라고 컴파일러에게 약속하는 것
-
특수화에서 인터페이스를 제공하지 않으면 컴파일 에러가 발생한다.
- 기본 클래스 멤버에 대한 참조가 무효한지 컴파일러가 진단하는 과정의 위치
- 파생 클래스 템플릿의 정의가 구문분석 될 때(이른 진단)(지금 항목 상황일 때)
- 파생 클래스 템플릿이 특정한 템플릿 매개변수를 받아 인스턴스화 될 때(기본)