템플릿(Template)이란
- 함수나 클래스를 개별적으로 다시 작성하지 않아도, 여러 자료 형으로 사용할 수 있도록 하게 만들어 놓은 틀
- 함수 템플릿(Function Template)와 클래스 템플릿(Class Template)으로 분류
// 같은 기능을 하는, 다른 타입을 인자로 받는 두 함수
int sum(int a, int b)
{
return a + b;
}
double sum(double a, double b)
{
return a + b;
}
// 아래의 하나의 함수로 변환 가능
// 함수 템플릿
template <typename T>
T sum(T a, T b)
{
return a + b;
}
// 배열 Vector를 직접 만든 경우,
// 어떤 타입이든지 받아 사용할 수 있음
// 클래스 템플릿
template <typename T>
class Vector {
T* data;
int capacity;
// ...
}
특수화
// 아래와 같은 클래스 템플릿 정의
template <typename A, typename B, typename C>
class test {};
// 일부만 특수화 하거나
// => 부분 특수화
template <typename B>
class test<int, B, double> {};
// 모든 특수화가 가능
// => 완전 특수화
template <>
class test<int, int, double> {};
// 위와 같은 특수화된 탬플릿에 대해
// 오리지널 클래스 템플릿과는 다른 별도의 처리를 수행
명시적 특수화
- 특정 타입의 매개변수에 대해 별도로 처리하고 싶을 때 사용
- 호출 순서
- 일반 함수 » 명시적 특수화 탬플릿 함수 » 탬플릿 함수
// 일반 함수
int add(int a, int b)
{
return a+b;
}
// 탬플릿 함수
template <typename T>
T add(T a, Tb)
{
return a+b;
}
// 명시적 특수화 탬플릿 함수
template<>
double add<double>(double a, double b)
{
return a+b;
}
...
int main()
{
// 명시적 특수화 add 함수를 호출
auto sum = add(10.1, 20.5);
}
탬플릿 클래스의 부분 특수화
// 아래와 같은 탬플릿 클래스가 있다고 할 때,
template <typename T1, typename T2>
class MyClass
{
...
};
// 아래와 같은 부분 특수화가 가능하다
// 1) 동일한 타입의 인자를 2개 받는 부분 특수화
template <typename T>
class MyClass<T, T>
{
...
};
// 2) 두 번째 인자를 정수형으로 받는 부분 특수화
template <typename T>
class MyClass<T, int>
{
...
};
// 3) 두 인자를 모두 타입의 포인터로 받는 부분 특수화
template <typename T1, typename T2>
class MyClass<T1*, T2*>
{
...
};
// 아래와 같이 선언했을 때,
MyClass<int, int> m; // MyClass<T, T>와 MyClass<T, int> 긴의 모호성으로 인한 에러 발생
MyClass<int*, int*> m; // MyClass<T, T>와 MyClass<T1*, T2*> 긴의 모호성으로 인한 에러 발생
// 아래 부분 특수화를 선언해주면 해결 가능
template <typename T>
class MyClass<T*, T*>
{
...
};
탬플릿 함수의 부분 특수화
- 기본적으로 불가능하다
- 탬플릿 클래스의 멤버 함수 전체를 특수화할 수는 있으나, 일부만 특수화는 불가능하다
- 함수 탬플릿 자체도 부분 특수화는 불가능하다
비형식 매개변수
template<typename T, template<typename U, int I> class Arr>
class MyClass2
{
T t; // OK, T는 있습니다.
Arr<T, 10> a;
U u; //Error, U는 스코프 내에 없어요.
// Arr 클래스에서 받는 인자
// 위의 Arr<T, 10> a;에서 10 = U
};
구체화
암시적 구체화
- 일반적으로 탬플릿 함수를 구현하고 이를 사용할 때 발생
// 탬플릿 함수
template <typename T>
T add(T a, Tb)
{
return a+b;
}
int main()
{
// T = int로 하는 탬플릿 함수의 구현이 필요하다고 암시적으로 컴파일러에게 전달
add(10, 5);
}
명시적 구체화
- 탬플릿 함수의 정의를 이용하여 특정 타입을 사용하는 함수를 정의하라고 컴파일러에게 명시적으로 전달
- 이를 전달받은 컴파일러는 해당 함수의 사용 여부에 상관없이 정의 코드를 생성한다
- 원래는 탬플릿 함수를 정의하고, 이를 특정 타입에 대해 사용하는 경우에만 컴파일러가 코드를 생성한다
// 명시적 구체화
// 사용 여부에 상관없이 탬플릿 함수 정의 코드를
// 생성하도록 컴파일러에게 전달
template <typename T>
T add(T a, Tb)
{
return a+b;
}
int main()
{
// T가 double 인지 int 인지 애매함 -> 컴파일 에러 발생
add(10, 5.5);
// T = double로 하는 탬플릿 함수의 구현이 필요하다고 명시적으로 컴파일러에게 전달
add<double>(10, 5);
}
탬플릿 사용의 장단점
장점
- 타입마다 중복되는 코드를 줄일 수 있음
- 불필요한 타입 변형을 막을 방법은 없음 (-> 단점)
- 제네릭 함수를 정의할 때도 짧게 유지할 필요가 있음
- 컴파일 타임에 다형성을 부여할 수 있음
- 컨테이너에 여러 타입의 객체를 저장할 수 있음
단점
- 코드의 가독성을 떨어뜨리고 디버깅이 어려워질 수 있음
- 컴파일러가 컴파일 도중에 각 템플릿 인스턴스에 대한 코드를 생성
- 컴파일 타임이 더 많이 소요되고, 템플릿 매개 변수를 추가할 수록 더 느려짐
- 코드가 많아지면서 실행 파일의 크기가 너무 커져도 퍼포먼스에 영향을 줄 수 있음
출처