2009.07.25 20:56 책 정리/C++ Template

템플릿 인자는 템플릿이 인스터스화 될 때, 그 파라미터의 값을 말한다. 이 템플릿 인자는 다음 4가지 방식에 의해서 결정 될 수 있다.

1. 명시적 지정에 의한 결정

이렇게 명시적으로 말한것을 뜻한다.

2. 삽입된 클래스 이름에 의한 결정

3. 기본 템플릿 인자에 의한 결정

4. 인자 추론에 의한 결정

이건 부연 설명을 덧붙여야 하는데, 함수 템플릿만 인자 추론이 된다.

8.3.1 함수 템플릿 인자
기본적으로 함수 템플릿 인자의 경우 자동으로 인자 추론이 이루어 지지만, 경우에 따라서 인자 추론이 되지 않는 경우도 있다. 이 때는 명시적 템플릿 인자 결정을 통하여 인스턴스화 해야만 한다. 보통 함수 템플릿의 리턴 타입의 경우가 추론이 안되는데, 강조 이런 인자들은 대개 템플릿 파라미터의 맨 앞에 넣는다.
예를 보자.

여기서 볼 수 있듯이, 리턴 타입은 추론이 안되었다.

함수 템플릿은 오버로딩이 가능한데, 이 때 주의해야 하는 게 있다. 다음 예를 보자.

여기서 봐야 하는것은 multi의 경우 함수 오버로딩을 사용 했는데, apply 함수 템플릿에서 둘 중 한가지를 선택 할 수 없다는 것이다. 왜냐하면 어떤 multi 함수 템플릿을 호출해야 하는지 애매하기 때문이다. 이 애매함은 multi 중 첫번째도 적당하고 두번째도 적당하기 때문에 어떤 함수 포인터로 와야 하는지에 대한 애매함이다.

이러한 함수 템플릿 오버로딩 시스템이 단점으로 보일 수 있지만, 장점이 훨씬 더 강력하기에 충분히 커버된다. 다음 예를 봐보자.

이렇게 멤버가 있는지 없는지 체크 할 수도 있다. 굳! 여기서 짚어봐야 하는 것은 함수 오버로딩 해석 방식에서 인자를 생략한 파라미터를 바인딩 하는 것 보다, 0을 널포인터로 인식하는게 더 우선 순위가 높다는 것이다. 이러한 법칙에 대한 설명은 Modern C++ Design 2.6장에 자세히 설명되어 있으므로 생략한다.

8.3.2 데이터형 인자.
템플릿 인자로는 데이터형 파라미터가 올 수 있다. 그런데 이러한 데이터형으로 올 수 없는게 두가지 있다.

  1. 지역 클래스와 지역 열거형(enum)은 데이터형 인자로 올 수 없다.
  2. 이름이 붙여지지 않은 클래스형과, 이름이 붙여지지 않은 열거형은 데이터형 인자로 올 수 없다.

그래서 다음 예제를 msvc에서 실행 하였더니 잘되고 g++ 4.3 에서 해보았더니 되지 않았다. 이 차이도 알고 있으면 도움 될듯




8.3.3 데이터형이 아닌 인자
데이터형 말고 컴파일 타임에 일 수 있는 데이터자체가 올 수도 있는데, 다음 사항을 한가지라도 만족해야만 하는 데이터여야만 한다.

  1. 컴파일 시 결정되는 정수형 상수값
  2. 외부 변수나 함수의 이름앞에 주소연산자(&)가 붙은 경우, 단 함수나 배열의 경우 &는 생략될 수 있다.
  3. 멤버 접근 포인터 상수, 예) &class::member

예를 보면 쉽게 이해 할 수 있을 것이다.

이렇게 컴파일 잘 되는 것도 있지만, 다음과 같은 인자들은 컴파일이 되지 않는다.

  1. 널 포인터 상수
  2. 부동소수점 숫자
  3. 문자열 리터럴

3번의 경우는 꽁수로 가능하긴 하다. 문자열 리터럴을 배열에 저장하고 그 배열의 포인터를 템플릿 인자로 넘기면 되기 때문이다. 이때 주의해야 하는 것은 배열이 const 일 경우, 내부링크를 갖을 수 있기 때문에, 템플릿 인자로 쓰일 수 없다는 점이다. 다음 예제를 봐보자.

이때 extern char const Hello2[] = "Hello, world"; 라고 해주면 잘 컴파일 될 것이다.

그런데 도데체 왜 내부링크를 갖는건 안되는거지?
C++ 에선 단일 정의 원칙이 있다. 그러므로 전역 변수이름을 동일한것으로 절대 만들 수 없다. 하지만 const 를 사용하면, static 선언 처럼, 해당 파일의 범위에서 독자적인 메모리 영역으로 올라가서 쓰인다. 그래서 변수명이 똑같아도 잘 컴파일되고 링크된다는 것이다. C++ 에서 const가 C에서의 const와 이 부분이 명확하게 바뀌었던 것이다.

그러므로 위의 예제처럼 Hello2 가 다른 파일에도 정의되어 있다면, 두개의 파일에서 Hello2를 템플릿 인자로 받게 될 때, 서로 다른 템플릿 인스턴스가 일어나므로, 템플릿이 오작동할 위험성을 갖게 되는 것이다.

그래서! 컴파일러는 "내부 링크를 갖는 것은 템플릿 인자로 올수 없다!" 라고 못박아 두는 것이다.

8.3.4 템플릿 템플릿 인자.
템플릿 템플릿 파라미터를 사용 할 때, 주의 해야 할 것은 갯수를 맞추어 주어야 한다는 것이다.

즉, 템플릿 기본 매개변수가 있다고 해서 생략 할 수 있는게 아니라는 것이다. 매개변수 목록을 반드시 동일하게 맞추어 주어야 한다. 그러므로 다음과 같이 짜야만 한다.

하지만 계속 쓸때마다 2번째 파라미터를 지정해 줘야 하는 불편함이 있다면 다음과 같이 기본 템플릿 파라미터를 줄 수도 있다.

그런데 템플릿 템플릿 파라미터를 선언 할 때, 항상 class 를 써야만 할까?
그렇다. 키워드는 class만 된다. 그렇다고 해서 템플릿 인자로 class 타입들만 올 수 있는건 아니다. union, struct 도 올 수 있으니 걱정할 필요가 없다.


8.3.5 템플릿 파라미터의 동일성
동일성이라고 하니, 어렵고, 그냥 같다. 라고 표현해도 좋다. 그렇다면 파라미터가 같다는 건 무슨 의미일까? .. 역시 코드를 보는게 빠르다.

그러므로 클래스에서 생성자 템플릿으로 만들어진 복사 생성자는 기본 복사 생성자가 될 수 없다

posted by 농사를 짓는 게임 프로그래머 최익필

댓글을 달아 주세요