-
[C++] 템플릿C++/이것이 C++이다 2021. 10. 26. 17:22
템플릿은 모양을 찍어내는 틀과 같습니다.
Template
1. [명사] 형판(形板)
2. [명사] 견본, 본보기C++에서의 템플릿도 위와 유사한 의미로 사용됩니다.
한 코드를 찍어내듯이 가져다 사용하는 템플릿은, C++의 생산성에 많은 기여를 하는 문법입니다.
이번 글에서는 템플릿을 사용하는 방법에 대해 살펴볼 것 입니다.
1. 템플릿
더보기서론에서 템플릿을 틀에 비유했습니다.
이 틀로 찍어내는 것은 바로 자료형 (클래스) 입니다.
템플릿은 함수, 클래스에 적용할 수 있으며 다음과 같은 문법을 사용합니다.
template<typename [Name]> [Return type] [function name]([Params]) { } template<typename [Name]> class [Class name] { };
관련된 예제와 함께 보겠습니다.
#include <iostream> using namespace std; template<typename T> T add(T a, T b) { return a + b; } template<typename T> class MyClass { public: MyClass(T param) : data(param) {} T get_data() const { return data; } operator T() { return data; } private: T data; }; int main() { MyClass<int> a(10); MyClass<const char*> b("Hello"); cout << a << "\n"; cout << b << "\n"; cout << add<int>(1, 2) << "\n"; return 0; }
10 Hello 3
위의 예제와 같이, 한 클래스 정의로 int, char*에 대응하는 인스턴스를 생성했습니다.
템플릿을 이용하면 이렇게 상황에 따라 알맞는 인스턴스를 생성할 수 있습니다.
typename은 여러 개가 되거나, 기본 형식이 정해지거나, 다른 형식으로 사용할 수도 있습니다.
관련된 예제를 살펴보겠습니다.
#include <iostream> using namespace std; template<typename T = int> T add(T a, T b) { return a + b; } template<typename T1, typename T2> void print(T1 a, T2 b) { cout << a << " " << b << "\n"; } template<typename T, int val> void function(T a) { for (int i = 0; i < val; i++) { cout << a << " "; } } int main() { cout << add(1, 2) << "\n"; print(1.2, "Hello"); function<const char*, 3>("World"); return 0; }
3 1.2 Hello World World World
main함수에서 호출된 순서대로 살펴보면
add함수는 템플릿을 사용했지만 기본 자료형이 정해져 있어서 선언 없이 add(1,2)가 문제없이 작동했습니다.
print함수는 T1, T2라는 두 개의 템플릿을 사용하여, 각각 double과 char*형의 자료형이 사용되었습니다.
function함수는 템플릿에 사용된 int val로 반복문을 구성했습니다.
위와 같이, 템플릿은 여러 상황에 알맞게 대응할 수 있도록 프로그램을 작성할 수 있고
그것이 곧 생산성의 향상으로 이어질 수 있습니다.
2. 템플릿 특수화
더보기위 문단을 통해, 템플릿을 이용해 여러 자료형, 클래스에 대응하는 함수, 클래스를 만들 수 있다는 것을 알게 되었습니다.
하지만, 특별한 형식에 대하여 다르게 작동해야 하는 기능이 필요할 수 있습니다.
예를 들어 두 개의 매개변수를 더하는 함수에 대하여, 정수와 실수에 대한 연산과, 문자열에 대한 연산은 서로 달라야 할 것입니다.
템플릿 특수화는 그런 특수한 상황에 적용되는 문법입니다.
예제를 살펴보겠습니다.
#include <iostream> using namespace std; template<typename T> T add(T a, T b) { return a + b; } template<> const char* add(const char* a, const char* b) { int len_a = strlen(a); int len_b = strlen(b); char* result = new char[len_a + len_b + 1]; strcpy_s(result, len_a + 1, a); strcpy_s(result + len_a, len_b + 1, b); return result; } int main() { cout << add<int>(2, 3) << "\n"; cout << add<const char*>("Hello,", "World!") << "\n"; return 0; }
5 Hello,World!
main함수에서 두번째 호출된 add<const char*>함수는 특수화 된 함수를 호출한 것을 볼 수 있습니다.
이 함수의 정의부에 typename을 기술하지 않았는데, tempalte<typename T>로 변경해도 문제없이 컴파일됩니다.
그 외에 주의해야 할 점은, 특수화 된 함수는 원형과 템플릿의 구조가 같아야 한다는 것 입니다.
위 add함수의 경우는 반환 형식, 매개변수 2개가 모두 T로 되어있습니다.
따라서 특수화하는 함수 또한 반환 형식과 매개변수 2개의 형식이 같아야합니다.
매개변수를 나누려면 함수 원형의 템플릿의 개수를 늘려주는 식으로 변경할 수 있습니다.
클래스 템플릿 또한 특수화가 가능합니다.
이 때는, 함수와는 선언 방식이 조금 달라집니다.
예제를 살펴보겠습니다.
#include <iostream> using namespace std; template<typename T> class MyClass { public: MyClass(T param) : data(param) {} T get_data() const { return data; } private: T data; }; template<> class MyClass<char*> { // Line 14 public: MyClass(char* param) : data(param) {} char* get_data() const { return data; } private: char* data; }; int main() { MyClass<int> a(10); MyClass<const char*> b("Hello"); cout << a.get_data() << "\n"; cout << b.get_data() << "\n"; return 0; }
10 Hello
Line 14라고 표시된 주석이 char*형식에 대하여 특수화 된 MyClass입니다.
특수화 하는 타입에 대하여 클래스 선언에 추가해주어야 합니다.
3. 템플릿 상속
더보기템플릿 클래스의 상속은 다른 클래스와 다르지 않지만
추가적으로 선언해야 할 부분이 있기 때문에 가볍게 짚어보도록 하겠습니다.
예제를 살펴보겠습니다.
#include <iostream> using namespace std; template<typename T> class MyClassAnc { public: MyClassAnc(T param) : data(param) {} protected: T data; }; template<typename T> class MyClassDes : public MyClassAnc<T> { // Line 13 public: using MyClassAnc<T>::MyClassAnc; T get_data() const { return this->data; } }; int main() { MyClassDes<int> a(10); cout << a.get_data() << "\n"; return 0; }
10
주석처리된 Line 13처럼, 상속 시 템플릿 형식을 같이 선언해주는 것 외에는 달라지는 점이 없습니다.
템플릿은 간결하지만 강력한 문법입니다.
여러 자료형에 대응하는 같은 기능을 만들 때, 템플릿의 사용 여부에 따라서 생산성, 가독성이 달라집니다.
C++는 Standard Template Libraries (STL, 표준 템플릿 라이브러리)을 제공하는데, 여기에는 여러 알고리즘, 자료구조 등이 템플릿을 통해 구현되어 있습니다.
이 STL에 대해서도 차후 다룰 수 있도록 하겠습니다.
다음 글은 스마트 포인터 입니다.
읽어주셔서 감사합니다.
'C++ > 이것이 C++이다' 카테고리의 다른 글
[C++] 예외 처리 (0) 2021.11.25 [C++] 스마트 포인터 (0) 2021.11.03 [C++] 객체 간 관계 (0) 2021.10.19 [C++] 다중 상속 (0) 2021.09.24 [C++] 형 변환 연산자 (0) 2021.09.19