강께르의 개발일지

[C++] 템플릿에 대해(함수 템플릿) 본문

프로그래밍/C++

[C++] 템플릿에 대해(함수 템플릿)

강께르 2021. 6. 17. 23:57

1. 템플릿이 무엇인가?

- 열혈 C++ 프로그래밍에 적힌 설명이 매우 좋아서 이를 풀어 쓰려고 한다.

- 템플릿은 모형자라는 뜻을 가지고 있다. 모형자에는 틀로 모형이 정해져 있고, 사용자는 그 모형틀에 원하는 색의 펜을 넣고 따라 그리면 된다.

- 그 모형자의 특징을 풀어 쓰자면 이렇게 표현할 수 있을 것이다.

 

모형을 만들어 낸다. 모형의 틀은 결정되어 있지만, 모형의 색은 결정되어 있지 않아서 결정해야 한다.

 

- 이 말을 템플릿에게 적용하면 어떨까? 템플릿 중 함수 템플릿을 예로 말을 풀어 써보자.

 

함수 템플릿은 함수를 만들어 낸다. 함수의 기능은 결정되어 있지만, 자료형은 결정되어 있지 않아서 결정해야 한다.

 

- 이 말은 함수의 기능이 되는 몸체는 정의되어 있지만, 반환형이나 매개변수가 쓸 자료형이 결정되어 있지 않다는 것이다. 그래서 어느 자료형을 쓸 것인지 결정해야 한다는 것이다.

- 어느 자료형으로 쓸 것인지 결정해야한다는 뜻은 어떤 자료형이 들어와도 그 자료형에 맞게 함수의 기능이 되는 몸체를 쓸 수 있다는 것이다. 그러면 이런 생각이 들거다. "잉? 언제 자료형을 결정할 건데요?"

 

2. 함수 템플릿

- 위의 의문을 해결하기 위해 함수 템플릿을 어떻게 사용하는지 알아보자. 함수 템플릿을 정의한다면 다음과 같은 구문이 될 것이다.

// 일반적인 함수
int Add(int num1, int num2)
{
	return num1 + num2;
}

// 함수 템플릿
template <typename T>
T Add(T num1, T num2)
{
	return num1 + num2;
}

- 위의 구문을 이해하겠는가? 일반적인 함수는 익히 알고 있는 두 매개변수의 더한 값을 반환하는 함수이다.

- 이 함수를 템플릿으로 만들면 그 아래와 같은 코드가 될 것이다. 이것이 함수 템플릿인데 지금 보이는 특징으로는 함수 정의보다 위에 template <typename T>라는 구문이 있고, Add라는 함수 이름 앞의 자료형이 함수 정의 위에 있던 T가 있으며, 매개변수의 자료형도 T로 대체되어 있는 것을 볼 수 있다. 그리고 함수의 몸체는 그대로인 것도 확인 가능하다.

- 위 함수 템플릿의 의미는 자료형은 지금 정하지 않고 함수를 정의하겠는데, 나중에 이 함수를 호출할 때, 자료형을 정하겠다는 의미이다. 즉, Add 함수는 어떤 자료형을 반환하거나, 매개변수로 전달할지는 모르지만, 사용할 때 알려주겠다는 것이다. 그 때, 함수를 쓰는 목적은 두 매개 변수의 자료형이 어떻든 매개변수를 더하는 연산을 하겠다는 것이다.

// 함수 템플릿
template <typename T>
T Add(T num1, T num2)
{
	return num1 + num2;
}

int main()
{
	int num = Add<int>(10, 12); // 이 때, 자료형을 결정! 그에 맞는 자료형으로 함수를 호출!
}

- 실제 사용할 때 자료형을 알려주겠다는 의미는 위 코드와 같다. 함수를 직접 호출할 때, <>를 사용해 무슨 자료형을 쓸지 알려주는 것이다. 그 자료형에 따라서 함수를 호출하겠다는 것이다. 위의 코드는 int형 매개변수를 전달해 int형 반환값을 전달하는 함수가 될 것이다.

- <>에 int형을 넣으면 T를 int로 하는 함수를 호출할 것이고, double을 넣으면 T를 double로 하는 함수를 호출할 것이다.

- 그럼 template <typename T>는 무슨 의미일까? 그것은 T라는 이름을 이용해서 바로 아래에 있는 함수를 템플릿으로 정의하겠다는 의미이다. 그래서 함수 템플릿에는 template <typename T>와 같은 구문이 함께 사용되어야 한다.

- 그러면 의문이 드는 것이 있다. 함수를 템플릿으로 정의하여 호출하게 되면, 호출할 때마다 그거에 맞는 함수를 매번 새롭게 만들어서 호출되는 것인가? 비효율적으로 보이는데..?

- 그에 대한 답이다. 한번 만들어진 함수는 또 다시 만들지 않는다. 컴파일할 때, 함수에 따라서 하나씩 만든 함수로 다음 호출 때 다시 사용하게 된다. 함수는 자료형마다 하나씩 만들어 질 것이다. 컴파일 때 함수를 만든다고해서 컴파일 속도가 감소할 수 있겠지만 실행 속도에는 영향은 없다.

 

★ 참고할 것 : template <typename T>에서 typename 말고 class로 대신 사용할 수 있다.

예를 들어서 template <class T>로 사용할 수 있을 것이다.

 

- 조금 더 간단하게 함수 템플릿 사용할 방법은 없을까? <> 추가하여 T에게 자료형을 명시하는 것은 불편한 것은 맞다. 그래서 컴파일러는 다음과 같은 코드를 <> 추가한 것처럼 해석한다.

// 함수 템플릿
template <typename T>
T Add(T num1, T num2)
{
	return num1 + num2;
}

int main()
{
	double num = Add(10.15, 12.34); // 이 때, 자료형을 결정! 그에 맞는 자료형으로 함수를 호출!
}

- 컴파일러는 위 코드를 보고 다음과 같이 판단할 것이다.

 

매개변수를 보니 double형 매개변수가 들어왔다.

이것을 값의 손실 없이 전달하려면 함수 템플릿의 T가 double형 매개변수로 전달해야 겠다.

 

- <>를 통해서 자료형을 명시하는 방법으로 매개변수가 무엇이 들어오더라도 <>에 명시한 자료형으로 함수를 처리하는 방법이 있지만, 오로지 매개변수를 통해 간단하게 자료형을 알려주고 싶으면 위와 같이 하면 될 것이다.

 

2. 함수 템플릿과 템플릿 함수

- 여태까지 설명한 템플릿을 함수 템플릿이라고 이른다. 코드와 함께 명확히 알고 가자.

// 함수 템플릿
template <typename T>
T Add(T num1, T num2)
{
	return num1 + num2;
}

- 위의 템플릿을 기반으로 컴파일러가 컴파일 중에 만든 함수를 템플릿 함수라고 한다. 아래의 코드를 참고하자.

// 템플릿 함수
int Add<int>(T num1, T num2)
{
	return num1 + num2;
}

double Add<double>(T num1, T num2)
{
	return num1 + num2;
}

- 용어가 순서만 다를 뿐, 구분하기 어렵다. 이를 구분하고자 하는 의도로 의미를 정리해보겠다

 

함수 템플릿 : 함수를 만드는데 사용되는 템플릿

템플릿 함수 : 템플릿을 기반으로 만들어진 함수

 

- 이렇게 이해하면 되겠다. 부연 설명을 추가하자면 뒤에 있는 말이 본질이라고 생각하자. 파인애플 피자에서 본질은 파인애플이 아니고 피자이다. 양념치킨의 본질은 양념이 아니고 치킨인 것처럼 말이다.

 

- 템플릿 함수는 아무리 이름이 같더라도 일반 함수와 함께 공존할 수 있는데, 이는 불필요한 것이다. 다만 구분된다는 사실만 알아두자.

 

3. 둘 이상의 자료형에 대한 템플릿 정의하기

- 매개변수가 하나로 동일한 자료형을 쓰는 템플릿만 만들 수 있나? 프로그래밍을 하다보면 다양한 매개변수를 필요로 하는 함수를 정의하는 경우가 많다. 그런데 T 하나로 한 자료형 정의하는 함수 템플릿은 뭔가 아쉽지 않을까?

- 그래서 두 개 이상의 자료형에 대해 템플릿을 정의하는 방법을 아래 코드와 함께 보려고 한다.

// 함수 템플릿
template <class T1, class T2>
void ShowDate(T1 val1, T2 val2)
{
	cout << val1 << val2 << endl;
}

- 위의 코드에서 typename이 아닌 class를 썼다고 해서, 둘 이상의 자료형은 class를 사용해야한다는 말이 아니다. typename이 아닌 class로 템플릿을 정의할 수 있다는 것을 알려주기 위함이다.

- 두 개의 typename을 선언하고 그 typename의 이름을 달리해 쓰고자 하는 매개변수 혹은 반환형, 형 변환의 자료형으로 사용하면 된다.

- 형 변환으로 사용할 수 있다. 사용하고자 하는 변수에 (형변환하고자 하는 템플릿 자료형)을 앞에 붙여주면 된다. 보통 형 변환처럼 자료형을 사용하면 된다.

 

4. 함수 템플릿의 특수화

template <class T>
T max(T a, T b)
{
	return a > b ? a : b;
}

cout << max(11, 15);
cout << max('T', 'Q');
cout << max("Simple", "Best");

- 위의 함수 템플릿은 세 개의 템플릿 함수를 컴파일 과정에 만들 것이다. <int>, <char>, <char*>일 것이다. 이 때, 정수형은 값의 대소 비교를, 단일 문자형은 아스키 코드값의 대소 비교를, 문자열은 주소값 비교를 할 것이다.

- 그런데 max라는 함수 템플릿을 통해 만들어진 <char*> 템플릿 함수가 맘에 안 든다. 문자열을 매개변수로 줘서 두 문자열의 길이를 비교하는 목적으로 max를 호출하거나, 사전 편찬 순서로 가장 먼저 오는 것을 판단해주는 등 문자열과 관련된 비교를 원하는데 매번 바뀌는 주소값을 비교해주는 것은 정말 의미없는 함수이다.

- 그렇다면 어떻게 할까? 그 방안으로 사용되는 것이 함수 템플릿의 특수화라는 것이다.

- 아래와 같이 정의하면 <char*>에 대한 템플릿 함수가 따로 만들지 않고 정의된 것을 사용할 것이다.

template <class T>
T max(T a, T b)
{
	return a > b ? a : b;
}

template<>
char* max(char* a, char* b)
{
	return strlen(a) > strlen(b) ? a : b;
}

cout << max(11, 15);
cout << max('T', 'Q');
cout << max("Simple", "Best");

- <char*>에 대해서 따로 정의하는 것으로 특수화한 것이다. 컴파일러에게 템플릿을 통해 따로 만들지 말고 정의된 것을 사용하라고 하는것이다.

- 이 때에도 <>은 생략한 모양이며, 자료형 정보를 명시하고자 하는 목적으로 특수화한 함수 이름 뒤에 <자료형>을 추가해주면 좋다.

 

- 다음은 클래스 템플릿에 대해 정리해보겠다.

 

'프로그래밍 > C++' 카테고리의 다른 글

[C++] STL에 대해  (0) 2021.06.18
[C++] 템플릿에 대해(클래스 템플릿)  (0) 2021.06.18
[C++] 연산자 오버로딩에 대해_2  (0) 2021.06.17
[C++] friend 키워드  (0) 2021.06.16
[C++] 연산자 오버로딩에 대하여_1  (0) 2021.06.16