template <typename Company>
class MsgSender<Company>
{
public:
void sendClear(const MsgInfo& info)
{
동작...
}
}
template<typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
void sendClearMsg(const MsgInfo& info)
{
sendClear(info);
}
}
위의 코드는 컴파일되지 않는다.
그 이유는 LoggingMsgSender 클래스가 컴파일될 때 Base클래스인 MsgSender의 템플릿 매개변수가 어떤 것인지 모르기 때문에(LoggingMsgSender가 인스턴스로 만들어 질 때까지 무엇인지 알 수 없다) MsgSender<Company> 클래스가 어떤 형태인지 알 수 없어서 sendClear 함수가 들어 있는지 없는지 알아낼 방법이 없기 때문입니다.
Base 클래스인 MsgSender에 sendClear 함수가 버젓이 있는데 무슨 소리인지 이해가 가지 않는 분들도 계실겁니다. 왜 컴파일러에서 이런 제약을 걸게 되었는지에 대해서는 여러 원인들이 있겠지만 한가지 예는 완전템플릿 특수화가 있습니다.
- 완전 템플릿 특수화(total template specialization)
template<>
class MsgSender<CompanyZ>
{
public:
void sendSecret(const MsgInfo& info)
{
...
}
}
MsgSender클래스가 전달받은 템플릿 매개변수가 CompanyZ 일 때 쓸수 있도록 특수화된 버전입니다.
위와 같이 특수화된 버전의 MsgSender의 경우 SendClear함수가 아예 존재하지 않습니다. MsgSender<CompanyZ>를 상속받은 LoggingMsgSender의 경우 sendClear 함수를 호출 할 수 없기 때문에 위와 같이 템플릿으로 만들어진 기본클래스의 함수 호출을 컴파일러에서 막아놓은 것입니다.
위의 코드를 컴파일 가능하게 바꾸는 방법은 세 가지가 있습니다.
1. 기본 클래스 함수에 대한 호출문 앞에 this->를 붙입니다.
template<typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
void sendClearMsg(const MsgInfo& info)
{
this->sendClear(info); //sendClear가 상속되는 것으로 가정
}
}
2. using 선언을 사용합니다.
template<typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
using MsgSender<Company>::sendClear; //컴파일러에게 sendClear 함수가 기본 클래스에 있다고 가정하라고 알려준다.
void sendClearMsg(const MsgInfo& info)
{
sendClear(info);
}
}
3. 호출할 함수가 기본 클래스의 함수라는 점을 명시적으로 지정합니다.
template<typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
void sendClearMsg(const MsgInfo& info)
{
MsgSender<Company>::sendClear(info); //sendClear가 상속되는 것으로 가정
}
}
위의 세가지 방법들은 모두 동작 원리가 같습니다. 기본 클래스 템플릿이 이후에 어떻게 특수화되더라도 원래의 일반형 템플릿에서 제공하는 인터페이스를 그대로 제공할 것이라고 컴파일러에게 약속하는 것입니다.
LoggingMsgSender<CompanyZ> zMsgSender;
MsgInfo msgData;
zMsgSender.sendClearMsg(msgData);
혹시나 위와 같이 약속을 어기게 되더라도 CompanyZ라는 템플릿 매개변수를 전달받아 인스턴스화될 때 MsgSender<CompanyZ>에는 sendClear라는 함수가 없다는걸 컴파일러가 알아 챌 수 있으므로 컴파일이 불가능합니다.
파생 클래스 템플릿의 정의가 구문분석될 때 ( C++ 기본 정책)
-> 파생 클래스 템플릿이 특정한 템플릿 매개변수를 받아 인스턴스화될 때
로 진단시점이 변경되었다.
결과적으로 기본 클래스 멤버에 대한 참조가 무효한지에 대한 컴파일러의 진단 시점이 변경된 것이라고 볼 수 있습니다.
'이론 > [책]Effective C++' 카테고리의 다른 글
[항목55]Boo자유친! 부스트를 늘 여러분 가까이에 (0) | 2018.01.02 |
---|---|
[항목45]호환되는 모든 타입을 받아들이는 데는 멤버 함수 템플릿이 직방! (0) | 2017.12.28 |
[항목44]매개변수에 독립적인 코드는 템플릿으로부터 분리시키자 (0) | 2017.12.26 |