책 정리/Effective C++ 3rd

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

ocean20 2020. 3. 19. 10:38

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