1. 결론
1) 기본 클래스 템플릿은 언제라도 특수화 될 수 있다.
2) 컴파일러는 특수화버전의 인터페이스와 기본클래스 템플릿과 같을 수 없다는 것을 인식한다.
3) 기본클래스 템플릿을 상속받은 파생클래스 템플릿은 2)과 같은이유로 기본클래스안의 이름을 접근하지 않는다.
* 즉, 템플릿 기반클래스를 상속받을때에는 상속의 기본메커니즘(컴파일타임 상속체계)이 끊긴다.
4) 따라서, 기본클래스 템플릿안의 이름에 접근하는 방법을 알아둬야한다.
예제 1) 기본 클래스 템플릿
class CompanyA{ //회사 A
public :
...
void sendCleartext(const string& msg);
void sendEncrypted(const string& msg);
...
};
class CompanyB{ //회사 B
public :
...
void sendCleartext(const string& msg);
void sendEncrypted(const string& msg);
...
};
class MsgInfo { ... }; // 메시지 생성을 위한 클래스
template<typename Company>
class MsgSender{ // 메시지 전달을 위한 기본 클래스 템플릿
public :
...
void sendClear(const MsgInfo& Info) //해당 클래스를 상속받을 클래스는 기본클래스안의 sendClear()가 있는지 확인하지 않는다.
//그 이유는 MsgSender 기본 클래스 템플릿을 특수화 할수 있기 때문이다.
//만약 특수화된 템플릿에 sendClear()가 없다면 파생클래스는 컴파일러는 어떻게 동작해야 하겠는가?
//따라서 사용자는 기본클래스 내 이름에 접근하는 방법을 알아야 한다.
{
string msg;
Company c;
c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& Info)
{
...
}
};
//여기까지는 컴파일에러를 발생시키지 않는다.
예제2) 기본클래스 템플릿을 상속받은 파생 클래스 템플릿
//기본클래스템플릿이었던 MsgSender를 상속받은 LoggingMsgSender 클래스템플릿
template<typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public :
...
void sendClearMsg(const MsgInfo& info)
{
//"메시지 전송전 로그 기록"
sendClear(info); // Error! 위 comment와 같은 이유로 에러발생
//"메시지 전송후 로그 기록"
}
};
* 풀이 : sendClear(); 에러발생, 그이유는 예제1)의 comment에서 확인해보자.
예제 3) 완전 특수화 템플릿
//만약 CompanyZ는 CompanyA, B와 달리 암호화된 텍스트 전달기능만 제공한다고 가정해보자.
class CompanyZ{
public :
…
void sendEncrypted(const string& msg); // companyZ는 암호화된 텍스트만 전송할 수 있다.
};
template<>
class MsgSender<CompanyZ> { // 완전특수화를 진행하였다. sendClear()만 빠지고 나머진 동일하다.
public :
…
void sendSecret(const MsgInfo& info)
{ … }
};
*풀이 : 우선 template <> 구문을 살펴보자. 괄호안에 아무것도 없는 template의 뜻은 '이건 템플릿도 아니고 클래스도 아니다' 라는 것이다. 위코드는 Msgsender 템플릿을 템플릿 매개변수가 CompanyZ일때 쓸수있도록 특수화한것이다.
예제4) 복습
이제 다시 위 코드를 다시 살펴보자.
template<typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public :
...
void sendClearMsg(const MsgInfo& info)
{
//"메시지 전송전 로그 기록"
sendClear(info); // Error! 위 comment와 같은 이유로 에러발생
//"메시지 전송후 로그 기록"
}
};
*풀이 : 이코드는 당연히 안되겠다. 만약 기본클래스가 MsgSender<CompanyZ>이면 이코드는 말이 안되기 때문이다. 즉, C++ 컴파일러는 템플릿으로 만들어진 기본클래스를 뒤져서 상속된 이름을 찾는 것을 거부한다.
2. 해결방법 3가지
1) 기본클래스 함수에 대한 호출문 앞에 "this->"를 붙인다.
2) using 선언을 하자.
3) 기본클래스를 명시하자. ( 비추한다. 그 이유는 가상함수 바인딩이 무시되기 때문이다.)
*3가지 방법들은 기본 클래스 템플릿이 특수화되더라도 원래의 일반형 템플릿에서 제공하는 인터페이스를 그대로 제공할 것이라고 컴파일러에게 약속하는것이다.
이것만은 잊지 말자!
- 파생클래스 템플릿에서 기본 클래스 템플릿의 이름을 참조할 때는, "this->"접두사로 붙이거나 기본 클래스 한정문을 명시적으로 써주자.
'책 정리 > Effective C++ 3rd' 카테고리의 다른 글
항목 45. "호환되는 모든 타입"을 받아들이는 데는 멤버 함수 템플릿이 직방! (0) | 2020.03.24 |
---|---|
항목 44. 매개변수에 독립적인 코드는 템플릿으로부터 분리시키자 (0) | 2020.03.24 |
항목 42. typename의 두 가지 의미를 제대로 파악하자 (0) | 2020.03.17 |
항목 41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성부터 (0) | 2020.03.17 |
항목 40. 다중 상속은 심사숙고해서 사용하자 (0) | 2020.03.16 |
댓글