함수 템플릿(Function Template)

최신 C++ 컴파일러들은 가장 최근에 추가된 기능인 함수 템플릿을 구현합니다.
함수 템플릿은 int형이나 double형과 같은 구체적인 데이터형을 포괄할 수 있는 일반형(generic type)으로 함수를 정의합니다.
어떤 데이터형을 템플릿에 매개변수로 전달하면, 컴파일러가 그 데이터형에 맞는 함수를 생성합니다.
함수 템플릿 : 함수를 만들기 위한 템플릿 (정의를 말함)
템플릿 함수 : 템플릿으로부터 만들어지는 함수 (생성된 함수를 말함)

해당 용어에 대해 좀 더 자세히 알고 싶다면(https://sks3297.tistory.com/entry/C-%ED%95%A8%EC%88%98%ED%85%9C%ED%94%8C%EB%A6%BF%EA%B3%BC-%ED%85%9C%ED%94%8C%EB%A6%BF%ED%95%A8%EC%88%98)

 

 

함수 템플릿 사용방법

template<typename T1,typename T2>
void Func1(T1 a,T2 b){

}

함수 선언(또는 정의) 위에  template를 쓰면 됩니다.
typename 키워드 대신에 class키워드를 사용해도 똑같은 의미입니다.

C++98 표준형이 키워드 typename을 추가하기 전까지 C++은 class키워드를 사용했습니다.
T는 많은 프로그래머들이 주로 쓰고 있으며 굳이 T가 아니고 C++의 이름 짓기 규칙만 준수한다면 상관이 없습니다.

 

함수템플릿의 장점

  • 수동으로 데이터형을 바꿀 필요가 없다.
  • 자동으로 바꿔주니 시간을 절약하고 코드의 신뢰성을 높여준다.
  • 다양한 데이터형에 대해 같은 알고리즘을 적용하니 여러 개의 함수를 만들 필요가 없다.

함수템플릿을 만들고 함수 호출하기

#include <iostream>
template<typename T> //template<class T>로 해도 된다.
void Swap(T & a, T & b) { //a,b서로 값을 바꾸는 함수
    T temp = a;
    a = b;
    b = temp;
}
int main(void) {
    int num1 = 10;
    int num2 = 100;
    float num3 = 9.99;
    float num4 = 222.11;
    Swap(num1, num2);
    Swap(num3, num4);
    std::cout << num1 << std::endl;
    std::cout << num2 << std::endl;
    std::cout << num3 << std::endl;
    std::cout << num4 << std::endl;
    return 0;
}

출력 결과 이미지

두 개의 변수값을 바꾸는 Swap함수를 정의하고 템플릿 설정을 했습니다.
템플릿을 설정하였으므로 Swap함수에서 T는 '일반형'이 됩니다.
호출시 인자의 데이터형에 따라 컴파일러가 함수를 자동으로 생성합니다.

Swap(num1, num2);

함수 호출 시 컴파일러는

void Swap(int & a,int & b) { //a,b서로 값을 바꾸는 함수
    int temp = a;
    a = b;
    b = temp;
}

위와 같은 코드의 함수를 자동으로 생성합니다.

함수 호출 이후 그다음 코드인

Swap(num3, num4);

도 num3,num4와 같은 데이터형인 float으로 함수를 생성합니다.

 

매개변수 타입을 명시적으로 전달하기

호출 과정에서 매개변수에 대한 타입을 명시적으로 전달이 가능한데 이때 아래와 같이 호출하면 됩니다.

Swap<int>(num1, num2);
Swap<float>(num3, num4);

함수 이름과 (사이에 꺽쇠를 사용해 타입을 명시적으로 써주면 됩니다.
그럼 T는 명시적으로 적은 타입이 됩니다.

명시적 인수 지정이라고도 합니다.

 

템플릿의 오버로딩

모든 데이터형이 항상 같은 알고리즘을 사용할 리는 없습니다.
템플릿의 오버 로딩도 확실하게 구분되는 함수 시그니처를 사용해야 합니다.(함수 오버 로딩 편 참고)

#include <iostream>
template<typename T> //template<class T>로 해도 된다.
void Swap(T & a, T & b) { //a,b서로 값을 바꾸는 함수
    T temp = a;
    a = b;
    b = temp;
}
template<typename T>
void Swap(T[] a, T[] b) { //원본 템플릿이랑 함수 시그니처가 다르므로 오버로딩 된다.
}
template<typename T>
void Swap(T[] a, T[] b, int a) { //마찬가지이다.
}
int main(void) {
    return 0;
}

그리고 템플릿을 설정한 함수는 굳이 매개변수가 일반형이 아닌 구체형(int, float, double 등)을 사용해도 문제없습니다.

C++에서는 오버 로딩된 함수가 여러 가지 일 겁니다.


이때 컴파일러가 함수를 선택하는 기준

  1. 호출된 함수명과 동일한 함수를 가지고 목록을 만든다.
  2. 함수 시그니처(매개변수의 개수)가 일치하고, 인자에 대해 암시적 변환이 가능한 것만 골라낸다.
  3. 가장 적당한 함수가 있으면 호출, 없으면 에러인 것이다.

템플릿 함수와 static 변수

템플릿 함수마다 서로 다른 값을 가지고 있습니다.

즉 int형으로 구체화된 템플릿 함수에서 1 증가했다고 float형으로 구체화된 템플릿 함수에도 증가되는 게 아닙니다.

#include <iostream>
template<typename T> //template<class T>로 해도 된다.
void Swap(T & a, T & b) { //a,b서로 값을 바꾸는 함수
	static int count = 0;
	T temp = a;
	a = b;
	b = temp;
	count++;
	std::cout << typeid(T).name() << " Count: " << count << "\n";
}
int main(void) {
	int num1 = 10;
	int num2 = 100;
	float num3 = 9.99;
	float num4 = 222.11;
	Swap(num1, num2);
	Swap(num3, num4);
	std::cout << num1 << std::endl;
	std::cout << num2 << std::endl;
	std::cout << num3 << std::endl;
	std::cout << num4 << std::endl;
	return 0;
}

출력 결과

이처럼 구체화된 타입이 같은 함수만 영향이 있습니다.

 

댓글을 달아 주세요