[C++] 함수 템플릿

dumdumer·2024년 10월 15일
0

C++

목록 보기
4/5

함수 템플릿(Function Template)

함수 템플릿은 템플릿을 사용한 함수로, 서로 다른 타입의 변수를 인자로 받더라도 동일한 함수의 기능을 수행하게 할 수 있다.

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 Instantiation)

함수 템플릿의 파라미터에 다른 타입들을 넣어도 함수가 같은 기능을 수행할 수 있는 이유는 무엇일까?

그 이유는 컴파일러가 템플릿 인스턴스화를 통해 각 타입에 맞는 함수를 생성하기 때문이다.
즉, 함수 템플릿은 일반화된 함수의 틀이고, 실제로 호출되는 함수는 컴파일 타임에 생성되는 인스턴스화된 함수이다.

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 Specialization)

템플릿 특수화는 특정한 조합의 템플릿(혹은 특정 타입)에 대한 구현을 따로 만들어 놓는 기능이다.

  • 부동소수점 반올림 오류 같은 특정 타입을 사용했을 때 발생할 수 있는 문제를 구현을 따로 만들어 놓아 방지할 수 있다.
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 Parameter)

✅ 템플릿 파라미터는 기본값을 가질 수 있다.

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

함수 템플릿의 장단점

⭕장점

  1. 일반화 프로그래밍의 장점과 같다. 코드 재사용성과 유연성을 높여준다.
  2. 타입 계산이 컴파일 타임에 진행되어서 런타임 속도가 빠르다.

⭕단점

  1. 코드를 읽기가 어려워진다.
  2. 템플릿은 모든 구분된 파라미터를 암시적으로 인스턴스화하기 때문에 컴파일 시간이 더 걸리고, 바이너리 크기를 키운다.

.
.
.
C++ 공부를 위해 작성된 글입니다. 오류가 있다면 지적해 주시면 감사하겠습니다.

참고자료
https://github.com/federico-busato/Modern-CPP-Programming (10. Templates_I)

profile
tik tok

0개의 댓글