도순씨의 코딩일지

C++ :: 템플릿(Template), 클래스 템플릿(class Template) 본문

𝐏𝐑𝐎𝐆𝐑𝐀𝐌𝐌𝐈𝐍𝐆/𝐂++

C++ :: 템플릿(Template), 클래스 템플릿(class Template)

도순씨 2020. 8. 31. 00:00

🌼 함수를 대상으로 템플릿 이해하기

함수 템플릿은 함수를 만들어내는 역할을 합니다. 함수의 기능은 결정되어 있지만, 자료형은 결정되어 있지 않습니다. 그러므로 자료형을 결정해주어야 합니다. 다시 한 번 말하자면 함수 템플릿은 함수를 만드는 도구입니다. 자바의 제네릭과 유사하죠. 예시를 통해서 함수 템플릿을 이해해봅시다.

 

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

 

위 함수의 정보를 요약해보면 다음과 같습니다.

💡 함수의 기능 : 덧셈

💡 대상 자료형 : int형 데이터

 

이러한 함수를 만들어낼 수 있는 템플릿은 다음과 같이 정의됩니다.

 

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

 

위 함수의 정보를 요약해봅시다.

💡 함수의 기능 : 덧셈

💡 대상 자료형 : 결정되어 있지 않음.

 

T는 자료형을 결정하지 않겠다는 의미로 사용한 것입니다. 그리고 다음과 같이 함수를 완성시킬 수 있습니다.

 

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

 

위의 코드에서 첫 번째 줄은 T라는 의미를 이용하여 아래의 함수를 템플릿으로 정의한다는 의미입니다. 여기서 typename 대신 class를 사용하여도 상관 없습니다. 둘 다 빈번하게 사용되는 표현입니다.

 

그렇다면 두 가지 종류의 함수를 만들어봅시다.

💡 int형 덧셈을 진행하는 Add 함수

💡 double형 덧셈을 진행하는 Add 함수

 

⭐️AddFunctionTemplate.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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;
    return 0;
}
cs

 

⭐️AddFunctionTemplate.cpp 실행결과

1
2
3
4
35
6.6
6
5.89
cs

 

그럼 함수를 템블릿으로 정의하면 매 호출순간마다 함수를 만들까요? 그렇지 않습니다. 한 번 함수가 만들어지면 그 다음에는 만들어진 함수를 호출할 뿐 새로 함수를 만들지 않습니다. 컴파일 속도의 감소가 발생하긴 하지만 실행 속도가 줄어드는 것은 아니기 때문에 크게 신경 쓸 요소는 아닙니다.

 

앞에서 보인 예제의 main 함수는 다음과 같이 변경해도 상관 없습니다.

1
2
3
4
5
6
7
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;
    return 0;
}
cs

 

컴파일러가 인자의 자료형을 판단하고 적절한 실행결과를 도출하므로 위와 같이 코드를 작성해도 좋습니다.

 

이렇게 만들어지는 탬플릿 함수는 일반 함수와 구분이 된다. 두 종류의 함수는 공존할 수 있습니다. 아래의 예제를 살펴보도록 합시다.

 

⭐️ TwoTypeAddFunction.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;
 
template <typename T>
T Add(T num1, T num2){
    cout << "T Add(T num1, T num2)" << endl;
    return num1 + num2;
}
 
int Add(int num1, int num2){
    cout << "Add(int num1, int num2)" << endl;
    return num1 + num2;
}
 
double Add(double num1, double num2){
    cout << "Add(double num1, double num2)" << endl;
}
 
int main(void){
    cout << Add(57<< endl;
    cout << Add(3.77.5<< endl;
    cout << Add<int> (57<< endl;
    cout << Add<double> (3.77.5<< endl;
    return 0;
}
cs

 

⭐️ TwoTypeAddFunction.cpp 실행결과

1
2
3
4
5
6
7
8
Add(int num1, int num2)
12
Add(double num1, double num2)
6.95329e-310
T Add(T num1, T num2)
12
T Add(T num1, T num2)
11.2
cs

 

🌼 둘 이상의 형(Type)에 대해 템플릿 선언하기

템플릿의 정의에도 다양한 선언이 가능할 뿐만 아니라, 둘 이상의 형에 대해서 사용할 수 있습니다.

 

⭐️ PrimitiveFunctionTemplate.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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.7);
    ShowData<shortdouble> (70.4);
    return 0;
}
cs

 

⭐️ PrimitiveFunctionTemplate.cpp

1
2
3
4
5
A, 65
C, 67
D, 68.9
69, 69.7
70, 70.4
cs

 

🌼함수 템플릿의 특수화(specialization)

⭐️ NeedSpecialFunctionTemplate.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
 
template <typename T>
 
T Max(T a, T b){
    return a > b ? a : b;
}
 
int main(void){
    cout << Max(1115<< endl;
    cout << Max('T''Q'<< endl;
    cout << Max(3.57.5<< endl;
    cout << Max("Simple""Best"<< endl;
    return 0;
}
cs

 

⭐️ NeedSpecialFunctionTemplate.cpp 실행결과

1
2
3
4
15
T
7.5
Best
cs

 

하지만 위 코드에는 한계가 있습니다. 문자열 반환에 있어서 옳은 결과를 반환하지 못합니다. 만약 순서의 비교가 목적이라면 다음과 같이 작성해야 합니다.

1
2
3
const char* Max(const char* a, const char* b){
    return strcmp(a, b) > 0 ? a : b;
}
cs

 

이와 같이 템플릿의 함수 구성방법에 예외를 둘 필요가 있습니다. 이 때 사용하는 것이 '함수의 특수화'입니다. 다음 예제를 살펴봅시다.

 

⭐️ SpecialFunctionTemplate.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <cstring>
using namespace std;
 
template <typename T>
T Max(T a, T b){
    return a > b ? a : b;
}
 
template <>
char* Max(char* a, char* b){
    cout << "char* Max<char*>(char* a, char* b)" << endl;
    return strlen(a) > strlen(b) ? a : b;
};
 
template<>
const char* Max(const char* a, const char* b){
    cout << "const char* Max<const char*>(const char* a, const char* b)" << endl;
    return strcmp(a, b) > 0 ? a : b;
}
 
int main(void){
    cout << Max(1115)             << endl;
    cout << Max('T''Q')           << endl;
    cout << Max(3.57.5)           << endl;
    cout << Max("Simple""Best")   << endl;
 
    char str1[] = "Simple";
    char str2[] = "Best";
    cout << Max(str1, str2)         << endl;
    return 0;
}
 
cs

 

⭐️ SpecialFunctionTemplate.cpp 실행결과

1
2
3
4
5
6
7
15
T
7.5
const char* Max<const char*>(const char* a, const char* b)
Simple
char* Max<char*>(char* a, char* b)
Simple
cs

 

11번째 라인과 17번째 라인에서 확인할 수 있듯 char* 형 함수와 const char*형 함수는 템플릿 함수가 필요한 경우에는 따로 제시를 해주고 있습니다.

 

🌼 클래스 템플릿

클래스 템플릿은 클래스를 템플릿으로 정의가 가능한 것을 뜻합니다. 예제를 통해서 알아봅시다.

 

⭐️ PointClassTemplate.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;
 
template <typename T>
class Point{
private:
    T xpos, ypos;
public:
    Point(T x = 0 , T y = 0) : xpos(x), ypos(y) {}
    void ShowPosition() const{
        cout << '[' << xpos << ", " << ypos << ']' << endl;
    }
};
 
int main(void){
    Point<int> pos1(34);
    pos1.ShowPosition();
 
    Point<double> pos2(2.43.6);
    pos2.ShowPosition();
 
    Point<char> pos3('P''F');
    pos3.ShowPosition();
    return 0;
}
cs

 

⭐️ PointClassTemplate.cpp 실행결과

1
2
3
[3, 4]
[2.4, 3.6]
[P, F]
cs

 

여기에서는 템플릿 함수를 호출할 때 <int>, <double>과 같은 자료형을 생략할 수 없습니다. 

 

🌼 클래스 템플릿의 선언과 정의의 분리

클래스 템플릿도 멤버함수를 클래스 외부에서 정의하는 것이 가능합니다. 예제 코드를 한 번 살펴봅시다.

 

⭐️ PointClassTemplateFuncDef.cpp 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;
 
template <typename T>
class Point{
private:
    T xpos, ypos;
public:
    Point(T x = 0, T y = 0);
    void ShowPosition() const;
};
 
template <typename T>
Point <T> :: Point (T x, T y) : xpos(x), ypos(y) {}
 
template <typename T>
void Point<T> :: ShowPosition() const {
    cout << '[' << xpos << ", " << ypos << ']' << endl;
}
 
int main(void){
    Point <int> pos1(34);
    pos1.ShowPosition();
 
    Point <double> pos2(2.43.6);
    pos2.ShowPosition();
 
    Point<char> pos3('P''F');
    pos3.ShowPosition();
    return 0;
}
cs

 

⭐️ PointClassTemplateFuncDef.cpp 실행결과

1
2
3
[3, 4]
[2.4, 3.6]
[P, F]
cs

 

📜 출처

윤성우(2010). 윤성우 열혈 C++ 프로그래밍. 오렌지미디어.

Comments