함수 템플릿은 템플릿을 사용한 함수로, 서로 다른 타입의 변수를 인자로 받더라도 동일한 함수의 기능을 수행하게 할 수 있다.
template<typename T> // or template<class T>
T add(T a, T b) {
return a + b;
}
int c1 = add(3, 4); // c1 = 7
float c2 = add(3.0f, 4.0f); // c2 = 7.0f
template<typename T>
와 같은 형식으로 함수 위에 작성하여 템플릿 함수를 나타낼 수 있다.
위 예시처럼 int나 float 파라미터를 받는 함수를 각각 오버로딩하지 않아도 템플릿 파라미터 T
에 다른 타입을 넣음으로써 같은 기능을 수행하는 것을 볼 수 있다.
함수 템플릿의 파라미터에 다른 타입들을 넣어도 함수가 같은 기능을 수행할 수 있는 이유는 무엇일까?
그 이유는 컴파일러가 템플릿 인스턴스화를 통해 각 타입에 맞는 함수를 생성하기 때문이다.
즉, 함수 템플릿은 일반화된 함수의 틀이고, 실제로 호출되는 함수는 컴파일 타임에 생성되는 인스턴스화된 함수이다.
template<typename T>
T add(T a, T b) {
return a + b;
}
add(3, 4); // int형 함수 구현 생성. int add(int, int)
add(3.0f, 4.0f); // float형 함수 구현 생성. float add(float, float)
add(2, 6); // 이미 생성됨.
템플릿 인스턴스화는 암시적 인스턴스화와 명시적 인스턴스화로 나뉜다.
암시적 인스턴스화는 컴파일러가 추론한 타입이나 명시적 템플릿 인자에 따른 코드를 생성하는 형태의 인스턴스화이다.
template<typename T>
void f(T a) {}
void g() {
f(3); // void f(int) 생성 : 암시적 템플릿 인스턴스화
f<short>(3.0); // void f(short) 생성 : 암시적 템플릿 인스턴스화
}
// void f(float) 같이 사용하지 않은 타입의 인스턴스화는 생성되지 않는다.
명시적 인스턴스화는 사용자가 직접 인스턴스화할 함수를 선언해주는 것이다.
template <typename T>
void f(T a) { cout << a << "\n"; }
template void f<int>(int); // void f(int) 생성 : 명시적 템플릿 인스턴스화
일반적인 함수와 마찬가지로 함수 템플릿 또한 오버로딩을 할 수 있다.
또한, 템플릿 파라미터도 함수 시그니처의 하나이기 때문에 오버로딩이 가능하다.
template<typename T>
T add(T a, T b) {
return a + b;
}
template<typename T> // 함수 파라미터를 오버로딩
T add(T a, T b, T c) {
return a + b + c;
}
template<int C, typename T> // 템플릿 파라미터를 오버로딩
T add(T a, T b) {
return a + b + C;
}
템플릿 특수화는 특정한 조합의 템플릿(혹은 특정 타입)에 대한 구현을 따로 만들어 놓는 기능이다.
template<typename T>
T comp(T a, T b) {
return a == b;
}
template<>
float comp(float a, float b) {
return std::abs(a - b) < 1e-9;
}
cout << comp<int>(1, 1); // "true"
cout << comp<float>(10.0000001f, 10.0f); // "true"
✅ 템플릿 파라미터는 기본값을 가질 수 있다.
template<typename T = int>
void f() { cout << typeid(T).name(); }
template<int A = 3, int B = 4>
void print1() { cout << A << ", " << B; }
template<int A = 3, int B> // 가능은 하지만 A에 기본값을 주는게 의미가 없음.
void print2() { cout << A << ", " << B; }
f(); // 'int'
f<float>(); // 'float'
print1(); // print `3, 4`
print1<2>(); // print '2, 4'
print1<1, 2>(); // print '1, 2'
print2<2, 5>(); // print '2, 5'
// print2<2>(); // error
// print2(); // error
✅ 템플릿 파라미터의 이름이 없을 수도 있다.
void f() {} // 일반 함수. 코드가 있음.
template<typename = void>
void g() {} // g()를 호출하지 않는 이상 코드가 생성되지 않음.
int main(){
g(); // generated
}
✅ 템플릿 파라미터는 앞서 선언된 템플릿 파라미터를 사용해 초기화 할 수 있다.
template<int A, int B = A + 3>
void f() {
cout << B;
}
template<typename T, int S = sizeof(T)>
void g(T) {
cout << S;
}
f<3>(); // B = 6
g(3); // S = 4
.
.
.
C++ 공부를 위해 작성된 글입니다. 오류가 있다면 지적해 주시면 감사하겠습니다.
참고자료
https://github.com/federico-busato/Modern-CPP-Programming (10. Templates_I)