일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 티스토리챌린지
- 오블완
- 루아
- 게임
- resource management class
- Vector
- 암시적 변환
- 예외
- more effective c++
- c++
- 참조자
- 언리얼
- 영화
- 영화 리뷰
- 스마트 포인터
- UE4
- operator new
- 다형성
- 메타테이블
- reference
- lua
- 반복자
- virtual function
- 상속
- effective stl
- implicit conversion
- exception
- Effective c++
- Smart Pointer
- 비교 함수 객체
Archives
- Today
- Total
스토리텔링 개발자
[Effective C++] 43. 템플릿 부모 클래스의 인터페이스에 접근하기 본문
728x90
항목 43. 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아두자
템플릿 클래스에서 생길 수 있는 문제
class CompanyA
{
public:
void sendCleartext(const string& msg);
void sendEncrypted(const string& msg);
...
};
class CompanyB
{
public:
void sendCleartext(const string& msg);
void sendEncrypted(const string& msg);
...
};
class MsgInfo { ... };
// 템플릿 기본 클래스
template<typename C>
class MsgSender
{
public:
void sendClear(const MsgInfo& info)
{
string msg = ... // info로부터 만든 msg
C c;
c.sendCleartext(msg); // sendCleartext 인터페이스를 요구
}
...
};
// MsgSender<C>를 기본 클래스로 가지는 템플릿 클래스
templace<typename C>
class LoggingMsgSender : public MsgSender<C>
{
public:
void senderClearMsg(const MsgInfo& info)
{
...
sendClear(info); // 이 코드로 인해 컴파일이 되지 않는다!!!
...
}
...
};
- sendClear를 찾을 수 없다는 것이 컴파일이 안 되는 이유이다.
문제 이유
- 기본 클래스인 MsgSender<C>에서 C는 템플릿 매개변수이고, 이가 명확하지 않은 시점에서는 sendClear 함수가 들어있는지 알아낼 방법이 없는 것이 문제이다.
- 아래와 같은 경우, sendClear가 없는 MsgSencer<C>라는 것이 존재할 수 있다.
// sendCleartext 함수를 제공하지 않는 클래스
class CompanyZ
{
public:
...
void sendEncrypted(const string msg);
...
};
// MsgSender 템플릿의 완전 특수화 버전
// sendClear 함수를 제외하고 특수화 해버렸다.
template<>
class MsgSender<CompanyZ>
{
public:
...
void sendSecret(const MsgInfo& info) { ... }
};
- 기본 클래스 템플릿은 언제라도 특수화 될 수 있고, 특수화 버전 인터페이스는 원래의 일반형 템플릿과 꼭 같으리란 보장이 없다.
- 즉, 객체 지향 C++과는 다르게 템플릿 C++ 에서는 상속 메커니즘이 다르다.
완전 템플릿 특수화
- 템플릿의 매개변수들이 하나도 빠짐없이 구체적인 타입으로 정해진 상태의 특수화.
해결 방법
- C++의 "난 템플릿화된 기본 클래스는 멋대로 안 뒤질 거야!" 가 발현되지 않도록 해야 한다.
- 해결 방법 세 가지
- 기본 클래스 함수에 대한 호출문 앞에 ‘this->’를 추가한다.
-
templace<typename C> class LoggingMsgSender : public MsgSender<C> { public: void sendClearMsg(const MsgInfo& info) { this->sendClear(info); // sendClear가 상속되는 것으로 가정한다. ... } };
-
- using 선언을 한다.
-
templace<typename C> class LoggingMsgSender : public MsgSender<C> { public: // 컴파일러에게 기본 클래스에 해당 함수가 있다고 가정하라고 알려준다. using MsgSender<C>::sendClear; void sendClearMsg(const MsgInfo& info) { sendClear(info); ... } };
- 항목 33과 비슷해 보이지만..
- 항목 33의 경우, 기본 클래스의 이름이 파생 클래스에서 가려져서 기본 클래스를 탐색하지 않는 문제.
- 해당 상황의 경우, 템플릿화된 기본 클래스는 아무 말이 없으면, 어떤 경우에도 탐색 유효범위에 들어가지 않는 문제.
-
- 호출 함수가 기본 클래스 함수라는 것을 명시적으로 지정한다.
-
template<typename C> class LoggingMsgSender : public MsgSender<C> { public: void sendClearMsg(const MsgInfo& info) { // sendClear 함수가 상속되는 것으로 가정된다. MsgSender<C>::sendClear(info); ... } };
- 하지만 이런 방법은 비추천.
- 호출되는 함수가 가상함수인 경우에는 가상 함수 바인딩이 무시되기 때문이다.
-
- 기본 클래스 함수에 대한 호출문 앞에 ‘this->’를 추가한다.
- 세 가지 방법 모두 동작 원리가 같다.
- 기존 클래스 템플릿이 어떻게 특수화 되더라도 원래의 일반형 템플릿에서 제공하는 인터페이스를 그대로 제공할 것이라고 컴파일러에게 약속하는 것이다.
- 즉, 특수화에서 인터페이스를 제공하지 않으면 컴파일 에러가 발생한다.
-
LoggingMsgSender<CompanyZ> zMsgSender; MsgInfo msgData; ... zMsgSender.sendClearMsg(msgData); // 에러!!!!
-
- 즉, 결국 기본 클래스 멤버에 대한 참조가 무효한지 컴파일러가 진단하는 위치를 정하는 것이 핵심이다.
- 파생 클래스 템플릿의 정의가 구문분석될 때
- 이른 진단(early diagnose)
- C++의 기본 정책
- 파생 클래스 템플릿이 특정한 템플릿 매개변수를 받아 인스턴스화 될 때
- 위 세 가지 방법을 통해 해결한 방법
- 파생 클래스 템플릿의 정의가 구문분석될 때
728x90
'개발 > Effective C++' 카테고리의 다른 글
[Effective C++] 45. 멤버 함수 템플릿으로 암시적 변환 지원 (1) | 2024.07.17 |
---|---|
[Effective C++] 44. 템플릿 코드 비대화 회피하기 (0) | 2024.07.16 |
[Effective C++] 42. typename의 두 가지 용법 (0) | 2024.07.12 |
[Effective C++] 41. 템플릿 프로그래밍 (0) | 2024.07.11 |
[Effective C++] 40. 다중 상속 (0) | 2024.07.10 |
Comments