2009.05.31 15:34 책 정리/C++ Template

개요
5.4 템플릿 템플릿 파라미터
5.5 0 초기화
5.6 함수 템플릿에서 문자열 리터럴을 인자로 사용


5.4 템플릿 템플릿 파라미터 ( template template paramater )
내가 보는 책은 "C++ Template" 이다. 이 책에 이번 주제인 template tempalte paramater 를 설명하고 있는데, 난 차례대로 "이것이 바로 템플릿 템플릿 파라미터이다" 라고 말해주는 코드를 컴퓨터에 입력 했다.

그리고 이것을 컴파일 해보면, 알겠지만, 클래스 템플릿의 두번째 템플릿 파라미터가 템플릿인 것이 보인다. 그래서 나는 컴파일을 시도 했다. 그러자 "클래스 템플릿 'error C3201: std::vector'에 대한 템플릿 매개 변수 목록이 템플릿 매개 변수 'CONT'에 대한 템플릿 매개 변수 목록과 일치하지 않습니다." 라고 에러를 우유먹고 베탈나서 화장실에서 쪼로록 볼일 보듯한 속도로 토해내고 있는 것이다.

10분간 책을 봐도 모르겠다. 정독을 더 해야 할까 고민도했다. g++ 에도 돌려 보니, 되지 않아서 무척 찜찜한 마음으로 다음장을 보니 "이 예제에서의 문제는 표준 라이브러리의 std::deque 템플릿이 하나 이상의 파라미터를 가진다는 데에 있다" 라고 적혀 있었다. 이 자식이 사람 때려놓고, 어느 손으로 때렸는지 물어 보네!

몇대 맞아보니 템플릿 템플릿 파라미터의 사정은 이러했다.
1. 템플릿 템플릿 파라미터는 템플릿 파라미터가 템플릿인 것을 말한다.
2. 템플릿 파라미터가 템플릿을 받는다고 정의해 주기 위해선 템플릿 파라미터 선언 구역에서 템플릿 키워드를 사용해야 하며, class 키워드로 이름을 정해 주어야 하낟.
3. 템플릿 파라미터가 템플릿일 경우, 파라미터인 템플릿의 파라미터를 모두 만족시켜 줘야 한다.
4. 템플릿 파라미터가 템플릿일 경우, 파라미터인 템플릿의 파라미터 이름을 생략해 줄 수 있다.

하나씩 코드로 옮겨 본다면
1.  template <typename T, template <typename ELEM> class CONT = std::deque > 에서 두번째 파라미터가 템플릿으로 선언되어 있는 것이 보인다.

2. template <typename T, template <typename ELEM> class CONT = std::deque > 에서 두번째 파라미터의 선언 시작은 template 로 해야 하며, 파라미터의 이름을 정할 때는 class 라고 해야 한다.

3. template <typename T, template <typename ELEM> class CONT = std::deque > 즉 이건 잘못 된 것이라는 것인데, deque는 2개의 템플릿 파라미터를 갖기 때문이다. 맞게 고친다면 template <typename T, template <typename ELEM, typename ALLOC> class CONT = std::deque > 이 되어야 한다.

4. <typename T, template <typename ELEM, typename ALLOC> class CONT = std::deque > 에서  <typename T, template <typename, typename> class CONT> 이렇게 생략 할 수 있는데, 내부에서 다시 템플릿으로 쓰이기 때문이다. 그러므로 갯수만 맞쳐 주면 된다.


1 ~ 4번의 사정으로 인하여, 정상적인 코드는 다음과 같다.

CONT<T, std::allocator<T> > m_; 를 보면 알겠지만, deque는 2개의 템플릿 파라미터를 갖기 때문에 저렇게 해야만 한다. 만약, 저것이 귀찮다면 다음과 같이 템플릿 기본 파라미터를 줄 수 있다. 그러므로 다음과 같은 코드도 된다.

템플릿 템플릿 파라미터의 파라미터(... 빌어먹을 재귀 법칙)를 보면, 두번째 파라미터 ALLOC 을 템플릿 기본 파라미터로 정의했으므로, Stack 내부에선 파라미터 1개를 생략 할 수 있다. 하지만 할당자를 외부에서 넣어 줄 수 없으므로, 책에선 나와 있지 않지만, 다음과 같이 템플릿 파라미터를 구성하는게 좋을 듯 싶다.

그렇다면 템플릿 템플릿 파라미터를 이용하면 무엇을 할 수 있는가?
 .. 코드를 생산해 낼 수 있다. .. 미안하다. 나도 이 활용 범위에 대해서 잘 모른다. 한번 써봤는데, 텍스트를 파싱하고 나서, 그 파싱된 결과물을 내가 원하는 컨테이너에 넣고자 하면서, 원하는 타입으로, 원하는 할당자를 편하게 하기 위해서 써봤다. 예를 들면

parsing.get< int, std::vector, std::allocator<int> >(L"ID"); 이런식으로 사용했다. 물론 get 함수 내부에선 모든 ID를 찾아서, vector에 표준 할당자를 이용하여, int형 값을 다 밀어 넣고 반환해준다. 많은 활용 범위가 있을 것이라고 생각 된다.

5.5 0 초기화
만약, 템플릿으로 넘긴 타입이 초기화가 될 때, 일반적인 초기화를 하고 싶다면, memset( ..) 미안하다 이건 쓰지 말자.. 초기화 함수를 사용하거나, 객체의 기본 생성자에서 초기화 해주는 로직을 넣어 주면 된다. 이 방법들은 가끔 기본형(fundametal type)까지도 포용력 있게 초기화를 해주지는 못한다. 다음 코드를 봐보자.

ikpil 클래스는 아주 잘 초기화 되겠지만, int형은 쓰레기 값이 들어 가는 것을 확인 할 수 있다.

이 문제를 어떻게 해결 하는가?
간단하다. C++ 에선 다음과 같이 foo()를 변경 하면 된다.

음, 만들고 나니 복사 생성자를 어떻게 해야 할까? 를 생각해야 한다. 그래서 다음 처럼, 아예 클래스만 받도록 할 수도 있다.

표현은 각자의 자유이니까, 사용 하고 싶은것을 사용하고 명세서를 달아 두면 될 듯도 하다. ㅋ : )

5.6 함수 템플릿에 문자열 리터럴을 인자로 사용
함수 템플릿에서 문자열 리터럴을 인자로 사용 하는건, 템플릿에서 현재 금기시 되고 있다. 왜냐하면 다음 예제를 실행해 보고, 컴파일러가 꾸역 꾸역 벹어내는 에러를 보면 알 것이다.
그러면 다음과 같은 에러를 볼 수 있다.

d:\projects\test\test\main.cpp(13) : error C2782: 'const T &max(const T &,const T &)' : 템플릿 매개 변수 'T'이(가) 모호합니다.
        d:\projects\test\test\main.cpp(4) : 'max' 선언을 참조하십시오.
        'const char [7]'일 수 있습니다.
        또는      'const char [6]'
d:\projects\test\test\main.cpp(14) : error C2782: 'const T &max(const T &,const T &)' : 템플릿 매개 변수 'T'이(가) 모호합니다.
        d:\projects\test\test\main.cpp(4) : 'max' 선언을 참조하십시오.
        'std::string'일 수 있습니다.
        또는      'const char [6]'

그렇다. 즐겨 사용하는 레퍼런스를 이용하면, 특정 배열의 포인터로 받게 되기 때문에, 서로 다른 타입이라고 인식이 된다 그래서 다음과 같이 max를 바꾸면 컴파일이 된다.

inline T const & max( T a, T b ) // 음~

이 문제를 해결하기 위한 여러가지 대안이 있는데, 다음과 같이 정리가 된다.
1. 참조자를 쓰지 않는다.
2. 참조자와 참조자가 아닌 파라미터를 모두 오버로딩 한다.
3. 실제 데이터형으로 오버로딩 한다.( std::string 으로 .. )
4. 배열형으로 오버로딩 한다.
5. max를 사용하기 전에 명시적으로 변환해서 사용 한다.

이 중에서도 가장 좋은 방법은, 3번이다. 나는 늘 std::string 으로 받아 왔는데, 퍼포먼스 테스트 해봐서, 과부하 걸리는 부분이 있다면, 그때 오버로딩을 추가하여 C 형태로 바꾸어도 되기 때문이다.


드디어 5장을 끝냈다. 맙소사 아직도 18장이나 남아있단 말인가?



저작자 표시
신고
posted by 농사를 짓는 게임 프로그래머 최익필

댓글을 달아 주세요