본문 바로가기
책 정리/Effective C++ 3rd

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

by ocean20 2020. 3. 19.

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->"접두사로 붙이거나 기본 클래스 한정문을 명시적으로 써주자.

댓글