2. ▶ 템플릿
컴파일 타임에 만들어지기 때문에 런타임 에러는 없음
▶ 템플릿 인스턴스화
클래스 템플릿에 템플릿 인자를 넘겨서 클래스 선언을 만드는 과정
* 책에서 쓰인 용어 정리
특수화 - 특수화 동작 자체
특수화 버전 - 특수화 과정을 통해 만들어진 결과
특수화 클래스 – 일반적인 특수화를 통해 만들어진 클래스나 함수
부분 특수화 템플릿 – 부분 특수화를 통해 만들어진 결과
3. ▶ 템플릿 매개변수
타입 매개변수
템플릿 매개변수 비템플릿 타입의 매개변수
템플릿 타입 매개변수
매개변수 개수 제한은 없음
비템플릿 타입의 인자는 보통 정수형으로 크기나 한계값을 둘 때 쓰임
template<class T, int max>
class Buffer
{
T v[max];
}
Buffer<char, 128> cbuf;
단 정수 타입의 템플릿 인자는 반드시 상수여야 한다
상수 표현식, 객체 / 함수의 주소, 멤버 포인터등을 인자로 넘길 수 있다
포인터가 넘어갈때는 &of(객체 또는 함수 이름) 형태나 함수 이름이어야 함
문자열 리터럴은 템플릿 인자로 허용되지 않음
4. ▶ 함수 템플릿
템플릿 사용의 큰 이유는 일반화 스타일의 컨테이너 클래스를 정의하는 것
인자를 통해 템플릿 인자를 추론
templat<class T> void sort(vector<T>&);
void f(vector<int>& vi, vector<string>& vs)
{
sort(vi);
sort(vs);
}
5. ▶ 함수 템플릿 인자
컴파일러는 함수 호출문을 보고 타입과 비타입 인자를 판단
-> 함수 인자 리스트와 템플릿 인자 집합과 맞아 떨어져야 함
template<class T, int max> T& lookup(Buffer<T,max>& b, const char* p);
class Record {};
Record& f(Buffer<Record, 128>& buf, const char* p)
{
return lookup(buf, p); //T가 Record, max가 128인 상태에서 lookup 호출
}
클래스 탬플릿 매개변수는 생성자 때문에 컴파일러가 추론하지 않음
-> 템플릿 특수화를 통해 클래스를 선택
6. ▶ 함수 템플릿 오버로딩
오버로딩 모호성 해결
1. 템플릿 특수화 함수를 검색
2. 더 가깝게 특수화된 템플릿 함수를 검색
3. 보통 함수와 템플릿 함수에 대해 모호성 해결 규칙 적용
4. 보통 함수와 템플릿 특수화가 남았다면 보통 함수 선택
5. 위 과정 후에 일치하는 것이 없거나, 함수가 두개 이상 남았다면 에러
7. ▶ 정책 설정을 위한 템플릿 인자 사용
template<class T>
class Cmp //보통의 경우
{
// 기본 비교
};
class Literate //스웨덴 인명
{
// 문자값에 기반한 결과 반환
};
void f(String<char> swede1, String<char> swede2)
{
compare< char, Cmp<char> >(swede1, swede2);
compare< char, Literate > (swede1, swede2);
}
자주 사용하지 않는 인자만 동작 기준을 명시적으로 설정할 수 있다
1. 오버로딩
2. 기본 템플릿 인자를 통해 자주 사용하는 경우 설정(좋은 방법)
8. ▶ 템플릿 특수화
템플릿 정의를 여러 개 준비해 놓고, 전달된 템플릿 인자에 따라 컴파일러가 결정
template<>
class Vector<void*>
{
void** p;
void*& operator[](int i);
}
Vector<void*>vpv;
9. ▶ 파생, 템플릿
파생과 템플릿은 공통 특성을 공유하는 코드를 작성할 수 있다
1. 비템플릿 클래스에서 템플릿 클래스 파생
-> 템플릿 집합에 대해 공통 코드 제공
template<class T> class Vector<T*> : private Vector<void*> { };
2. 기존의 클래스 템플릿에서 새로운 클래스 템플릿 파생
-> 기본 클래스 자체를 매개변수화
template<class T> class vector { };
template<class T> class Vec : public vector<T> { };
클래스 템플릿으로 만들어진 클래스는 일반 클래스와 다를바 없다
10. ▶ 매개변수화, 상속
템플릿은 어떤 타입 혹은 함수를 다른 타입을 사용해서 매개변수화하는 문법 기능
유사함
추상 클래스는 인터페이스를 정의할 수 있게 하는 문법 기능
* 사용 구별
객체 사이의 상속 등 계통 관계가 필요 없다면 템플릿 인자
객체들의 타입을 컴파일타임에 알 수 없다면 공통 추상 클래스에서 파생된 클래스 사용
런타임 효율, 함수의 인라이닝이 불가피하다면 템플릿 사용
11. ▶ 멤버 템플릿
클래스 혹은 클래스 템플릿 안에 템플릿인 멤버 포함 가능
단, 멤버 템플릿은 가상 함수가 될 수 없다 -> vtable 문제
▶ 클래스 템플릿
타입을 찍어내기 위한 틀로도 사용
동일한 템플릿에서 만들어진 클래스 사이에는 상속관계가 없음
-> 상속관계가 필요할 경우 포인터 템플릿을 통해 객체 사이의 상속관계를 반영, 멤버 템플릿
12. ▶ 템플릿 사용
1. 해석 단위 안에서 템플릿을 사용하는 위치 앞에다가 템플릿 정의를 인클루드
2. 해석 단위 안에서 템플릿을 사용하는 위치 앞에다가 템플릿 선언만 인클루드 후
정의는 별도 컴파일
1번의 경우 템플릿 함수를 인라인화 최적화 과정은 모두 컴파일러
컴파일러가 처리하는 정보의 양이 커지거나,
매크로등에 의해서 다른 선언까지 건드리게 될 위험 존재
2번은 비인라인 함수 처리
export 처리
비인라인 정적 멤버는 템플릿 안에 넣지 않는 것이 좋다
13. 고수의 조언
1. 많은 인자 타입에 적용할 수 있는 알고리즘을 작성하려면 템플릿을 사용할 것.
2. 컨테이너를 작성하려면 템플릿을 사용할 것.
3. 포인터를 담는 컨테이너를 구현할 때 코드 크기를 최소화하려면 특수화 버전을 제공할 것.
4. 템플릿의 특수화 버전 앞에 반드시 일반화 버전을 선언해 둘 것.
5. 템플릿 특수화 버전은 그것이 사용되는 지점 앞에 선언해 둘 것.
6. 템플릿이 인스턴스화되는 지점의 주변 환경에 대한 템플릿 정의의 의존도를 최소화하자.
7. 선언한 특수화 버전은 반드시 정의하자.
8. C스타일 문자열 및 배열에 대한 템플릿 특수화가 필요한지 생각할 것.
9. 무엇이든 매개변수화를 할 때는 정책 객체를 사용하자.
10. 타입은 다르지만 개념이 동일한 구현코드를 하나의 인터페이스를 통해 제공하려면 템플릿
특수화와 오버로딩을 사용하자.
11. 단순한 경우에 대해서는 단순한 인터페이스를 제공하고, 자주 등장하지 않는 경우에 대해
서는 오버로딩 및 기본 인자를 사용하자.
12. 어떤 클래스를 템플릿으로 일반화하기 전에 반드시 구체적인 클래스를 써서 디버깅하자.
13. 어떤 파일에 들어 있는 템플릿 정의를 다른 해석 단위에서 끌어오려면, 해당 템플릿 정의를
export로 만들어 두어야 한다.
14. 고수의 조언
14. 크기가 큰 템플릿 및 외부 의존 정보가 적지 않은 템플릿은 분할 컴파일 하자.
15. 타입 변환에 템플릿을 쓰는 것은 좋은데 각별히 주의를 기울이자.
16. 필요하다면, constraint()멤버함수를 써서 템플릿 인자에 제약을 가할 수 있다.
17. 컴파일 및 링크에 걸리는 시간을 최소화하려면 명시적 인스턴스화를 사용하자.
18. 런타임 효율이 가장 크게 문제되는 경우에는 파생 클래스보다 템플릿이 더 낫다.
19. 재컴파일 없이 새로운 타입을 추가해야할 경우에는 템플릿보다 파생 클래스가 더 낫다.
20. 공통 기본 클래스를 정의할 수 없는 경우에는 파생클래스 대신에 템플릿을 애용하자.
21. 기본제공 타입 및 구조체와의 호환성 제약이 중요한 경우에는 파생 클래스 대신에 템플릿
을 애용하자.