본문 바로가기
Language & Framework & GIT/C++

[씨앤씨뿔] 템플릿과 함수 템플릿

by veganwithbacon 2023. 6. 28.
반응형

  함수 템플릿

함수를 만드는 도구, 다양한 자료형의 함수를 만들어 낼 수 있다.

1
2
3
4
int Add(int num1, int num2)
{
  return num1 + num2;
}
cs

 - 위 함수의 기능 : 덧셈
 - 대상 자료형 : int형 데이터

위 함수를 템플릿 함수로 변경하자.

1
2
3
4
5
template <typename T>
T Add(T num1, T num2)
{
  return num1 + num2;
}
cs

 - template <typename T>
  : 이는 T라는 이름을 이용해서 아래에 선언된 함수를 템플릿으로 정의한다는 의미이다.
 - int을 T로 선언
  : T는 자료형을 결정짓지 않겠다는 의미로 사용한 것, 그래서 나중에 T를 대신해서 실제 자료형을 결정한다.

* typename을 대신해서 class를 사용해도 된다. 이 둘은 같은 의미이다. T대신 다른 문자를 사용해도 된다.
  - template <typename T>
  - template <class T>
  - C++ 초기엔 템플릿 선언에 키워드 class만 사용할 수 있었다. 그런데 이는 클래스 선언에 사용되는 키워드와 동일하다는 지적때문에  typename이라는 키워드도 사용할 수 있도록 정정되었다. 그러나 최근에 만들어진 C++관련 국내외 자료들을 보면 여전히 class가 많이 사용된다. 이유는 typename보다 class가 입력이 더 편해서이다. C++창시자도 class선언을 선호한다고 한다.

위와같이 템플릿 함수를 선언하게 되면, 컴파일러는 함수의 호출문장을 보면서 필요한 함수들을 만들어 낸다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
 
template <typename T>
 
T Add(T num1, T num2)
{
  return num1 + num2;
}
 
int main(void)
{
  cout<< Add<int>(1520<<endl;
  cout<< Add<double>(2.93.7<<endl;
  cout<< Add<int>(3.23.2<<endl;
  cout<< Add<double>(3.142.75<<endl;
}
cs

- 13행 : Add<int>(15, 20)
  : 여기서 <int>가 의미하는 바는 다음과 같다.
   "T를 int로 해서 만들어진 Add 함수를 호출한다."

위 문장을 보는 순간 컴파일러는 다음의 형태로 함수를 하나 만든다.
그리고 인자 15와 20을 전달하며 아래 함수를 호출하게 된다.

1
2
3
4
int Add(int num1, int num2)
{
  return num1 + num2;
}
cs

이어 13행의 Add<double>(2.9, 3.7)을 만나게 되면 아래 함수를 만든다.
1
2
3
4
double Add(double num1, double num2)
{
  return num1 + num2;
}
cs

* 컴파일러는 템플릿으로 정의된 함수를 호출하게 되면, 호출시 호출하는 형태로 함수를 생성한다. 그 다음 호출시엔 이미 만들어진 함수를 호출할 뿐 새로 함수를 만들지 않는다. 즉, 함수는 자료형당 하나씩만 만들어진다.
* 컴파일 시 함수를 생성하기 때문에 컴파일 속도는 느려진다. 그러나 실행속도가 느려진 것은 아니다.

위 main()함수는 아래와 같이 일반함수 호출과 같은 방식으로 수정이 가능하다.
이는 전달되는 인자의 자료형을 참조하여 호출될 함수의 유형을 컴파일러가 결정하기 때문이다.

1
2
3
4
5
6
7
8
int main(void)
{
  cout<< Add(1520<<endl;
  cout<< Add(2.93.7<<endl;
  cout<< Add(3.23.2<<endl;
  cout<< Add(3.142.75<<endl;
}
 
cs

**함수 템플릿과 템플릿 함수

  - 함수 템플릿(function template) : 앞서보인 다음의 정의를 가리켜 '함수 템플릿'이라 한다.

1
2
3
4
5
template <typename T>
T Add(T num1, T num2)
{
  return num1 + num2;
}
cs

 - 템플릿 함수(template function) : 위의 템플릿을 기반으로 컴파일러가 만들어 내는 다음 유형의 함수들을 '템플릿 함수'라 한다.
  (템플릿 함수는 컴파일러에 의해 생성된 함수이기 때문에 '생성된 함수(Generated Function)'으로도 불린다.

1
2
3
4
5
6
7
8
9
int Add<int>(int num1, int num2)
{
  return num1 + num2;
}
 
double Add<double>(double num1, double num2)
{
  return num1 + num2;
}
cs

 

* 둘 이상의 형(Type)에 대해 템플릿 선언하기
  - 함수 템플릿을 정의할 땐 다양한 자료형을 기본으로 선언할 수 있으며, 둘 이상의 형(type)에 대해서 템플릿을 선언할 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
 
template <class T1, class T2>
void ShowData(double num)
{
  cout<<(T1)num<<", "<<(T2)num<<endl;
}
 
int main(void)
{  ShowData<charint>(65);
  ShowData<charint>(67);
  ShowData<chardouble>(68.9);
  ShowData<shortdouble>(69.2);
  ShowData<shortdouble>(70.4);
  return 0;
}
cs

- 4행 : 쉼표를 이용해 둘 이상의 템플릿 타입을 명시, typename대신 class 키워드 사용
- 5행 : 함수 템플릿의 매개변수를 기본 자료형으로 선언할 수 있다.
- 7행 : 인자로 전달된 num의 값을 T1과 T2로 명시되는 자료형으로 형 변환해서 출력한다.
- 12~16행 : 위의 함수 템플릿은 매개변수 형이 double로 선언되어있어 전달되는 인자를 통해서 T1과 T2의 자료형을 결정짓지 못한다. 따라서 이러한 경우에는 템플릿 함수의 호출형식을 완전히 갖춰서 호출해야 한다.

[출력결과]
A, 65
C, 67
D, 68.9
69, 69.2
70, 70.4


  함수 템플릿의 특수화(Specialization)

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;

template <typename T>
T Max(const T a, const T b) {
    return a > b ? a : b;
}

 int main(const int argc, const char* const argv[]) { 
     cout << Max<int>(11, 15) << endl;
     cout << Max<char>('T', 'Q') << endl;
     cout << Max<double>(3.5, 7.5) << endl;
     cout << Max<const char*>("Simple", "Best") << endl;

     return 0;
}

/* 결과

15
T
7.5
Simple

*/

위 예제의 함수 템플릿 Max는 인자로 전달된 두 데이터 중 큰 값을 반환하도록 정의되어 있다. 그런데 문자열을 대상으로 호출할 경우, 그 결과에 대해서는 아무런 의미도 부여할 수 없게 된다 (단순히 주소 값의 비교결과가 반환). 

이렇듯 상황에 따라 템플릿 함수의 구성방법에 예외를 둘 필요가 있는데, 이 때 사용되는 것이 '함수 템플릿의 특수화(Specialization of Function Template)' 이다.

 

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


template <typename T>
T Max(const T a, const T b) {
    return a > b ? a : b;
}

// char* 형에 대해서 특수화
template <>
char* Max<char*>(char* a, char* b) {
    cout << "char* Max<char*>(char* a, char* b)" << endl;
    return strcmp(a, b) > 0 ? a : b;
}

// const char* 형에 대해서 특수화
template <>
const char* Max<const char*>(const char* a, const char* b) {
    cout << "const char* Max<const char*>(const char* a, const char* b)" << endl;
    return strlen(a) > strlen(b) ? a : b;
}


 int main(const int argc, const char* const argv[]) { 
     cout << Max<int>(11, 15) << endl;
     cout << Max<char>('T', 'Q') << endl;
     cout << Max<double>(3.5, 7.5) << endl;
     cout << Max<const char*>("Simple", "Best") << endl;

     char str1[] = "Simple";
     char str2[] = "Best";

     cout << endl;
     cout << Max<char*>(str1, str2) << endl;

     return 0;
}

/* 결과

15
T
7.5
const char* Max<const char*>(const char* a, const char* b)
Simple

char* Max<char*>(char* a, char* b)
Simple

*/

위의 예제에서 template<> 명령은 다음과 같다.

"char*형 함수는 프로그래머가 직접 제시를 하니, char*형 템플릿 함수가 필요한 경우 별도로 만들지 말고 프로그래머가 만든 템플릿 함수를 사용하라." 라는 메시지를 컴파일러에게 알리는 명령이다.

그리고 특수화하는 자료형 정보를 생략하건 생략하지 않건 그 의미하는 바에 차이는 없으나, 가급적이면 자료형 정보를 명시하는 것이 뜻을 명확히 하는 방법이 된다.

template <>
char* Max<char*>(char* a, char* b) { ... }

template <>
const char* Max<const char*>(const char* a, const char* b) { ... }

 

반응형

댓글