<함수형 템플릿>
예를 들어 두 정수 값을 받아 max를 출력해주는 함수를 하나 만들겠습니다.
| int max(int lhs, int rhs) { return (lhs > rhs ? lhs : rhs); } int main() { int a = max(10, 20); // 20 int b = max(20, 30); // 30 } | cs |
자 이제 int 와 double 을 비교해주는 함수가 하나 필요해졌습니다. 앗 double 과 double 을 비교해주는 함수도 필요해졌습니다.
| int max(int lhs, int rhs) { return (lhs > rhs ? lhs : rhs); } decltype(auto) max(int lhs, double rhs) { return (lhs > rhs ? lhs : rhs); } double max(double lhs, double rhs) { return (lhs > rhs ? lhs : rhs); } | cs
|
필요해지면 더 추가해야할까요? string 형과 int , string과 string, char 와 string 등등 비교할 타입이 많아질 때, max는 계속 오버로딩을 해줘야 할것입니다.
이럴때 template 을 사용하면 유용합니다. template 은 다양한 타입 내에서 동일한 알고리즘을 이용해야 할때 주로 사용됩니다.
| template <typename T, typename U> auto max(T lhs, U rhs) { return lhs > rhs ? lhs : rhs; } int main() { auto a = max(10, 25.3); // 25.3 auto b = max(10, 20); // 20 } | cs |
a 를 보시면, 10은 int 25.3은 double입니다. 저 값들은 max로 들어가
| template <typename int, typename double> auto max(int lhs, double rhs) { return lhs > rhs ? lhs : rhs; } | cs |
이렇게 치환됩니다. 인자의 타입 순서에따라 맞게 들어간다고 보시면 됩니다. T 와 U 는 & 또는 *로도 받을 수 있기 때문에, 큰 용량에 대해선 꼭 참조로 받으시는게 좋습니다.
| template <typename T, typename U> decltype(auto) max(const T& lhs, const U& rhs) { return lhs > rhs ? lhs : rhs; // const &로 받은건 rvalue도 올 수 있음 } | cs |
각 타입의 자료형을 달리하는게 아닌, 하나의 자료형으로 사용하거나 기본 자료형이 아닐 경우 템플릿을 구체화 시켜주어야 합니다.
| template <typename T> T max(T t1, T t2) { return t1 > t2 ? t1 : t2; } int main() { int a = max<int>(10, 25.3); // 두 인수를 다 int 형으로 변환함 double b = max<double>(10, 25.3); // 두 인수를 다 double 형으로 변환함 } | cs |
위와 같이 <int><double> 이 안붙을 경우를 암시적 구체화, 붙을 경우 명시적 구체화라고 합니다.
<클래스형 템플릿>
위와 같이 클래스 또한 template 으로 작성할 수 있습니다.
| template <typename T> class A { public: A(const T& rhs) : m_t(rhs) { } private: T m_t; }; int main() { A aa(10); // error, 무엇으로 class 를 만들어야 할 지 몰라요 A<int> bb(10); // ok, T가 int 인 A class를 만듭니다. } | cs |
클래스 내의 함수에 대해서도 템플릿 작성이 가능합니다.
| template <typename T> class A { public: A(const T& rhs) : m_t(rhs) { } template <typename U> void printsum(const U& rhs) { cout << m_t + rhs << endl; } private: T m_t; }; int main() { A<int> bb(10); bb.printsum(5.2); //15.2 bb.printsum<double>(5.2); //15.2 } | cs
|
<공통>
모든 템플릿은 부분 특수화와 완전 특수화가 가능합니다.
예를 들어, 바이트를 구하는 함수를 하나 정의해 보겠습니다.
| template <typename T> int mysize(const T rhs) { return sizeof(rhs); } int main() { int i = 0; double j = 0.0; cout << mysize(i) << endl; // 4byte cout << mysize(j) << endl; // 8byte } | cs |
그런데 char 형과 string 형에 대해선, 바이트가 아닌 길이를 알려주고 싶어졌습니다. 재 작성해야할까요?
답은 특수화에 있습니다. 우리가 일반 함수에서 인자타입에 따라 오버로딩 해줬듯이, 템플릿의 특수한 인자타입에만 맞는 템플릿을 재정의 해주는 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | template <typename T> int mysize(const T rhs) { return sizeof(rhs); } template <> int mysize<const char*>(const char* c) { return strlen(c); } template <> int mysize(const char* rhs) { return strlen(rhs); } // 위 함수와 동일합니다. int main() { int i = 0; double j = 0.0; cout << mysize(i) << endl; // 4byte cout << mysize(j) << endl; // 8byte cout << mysize("abcdedfg") << endl; // 8 } | cs |
위를 템플릿의 완전 특수화 라고 합니다. ( 함수 템플릿은 부분 특수화가 없습니다! )
그럼 클래스로 넘어가 보겠습니다.
| template <typename T, typename U> class A; template<> class A <int, double>; // 클래스 A에 대한 완전 특수화 template <typename T, typename U> class A<T, U*>; // 클래스 A에 대한 부분 특수화 template <typename T, typename U> class A<T*, U*>; // 클래스 A에 대한 부분 특수화 | cs
|
class 는 부분 특수화와 완전 특수화의 개념이 있습니다.
만약 임의의 자료형을 가지고 작성됐다면, 그 템플릿 클래스는 부분 특수화 입니다. 하지만 명확한 자료형으로 작성됐다면, 완전 특수화입니다. 하지만 임의의 자료형과 명확한 자료형이 같이 사용된다면 부분 특수화 입니다.
<매개변수>
Microsoft Docs는 파라미터에 대한 정보를 작성했습니다.
| template<typename T, size_t L> class MyArray { T arr[L]; public: MyArray() { ... } }; int main() { MyArray<MyClass, 10> arr; } | cs
|
위는 비형식 매개변수로, 임의의 자료형이 아닌 명확한 자료형도 올 수 있습니다.
| template<typename T, template<typename U, int I> class Arr> class MyClass2 { T t; // OK, T는 있습니다. Arr<T, 10> a; U u; //Error, U는 스코프 내에 없어요. }; | cs |
U는 그저 Arr의 인자값으로 사용된 것이기 때문에 MyClass의 스코프 내에선 보이지 않습니다.
이렇게 템플릿 매개변수로 템플릿이 올 수 있습니다.
그리고 템플릿은 기본 템플릿 매개변수를 지정 할 수 있습니다. 예를 들어 vector를 보면, 기본 템플릿 매개변수로 std::allocator가 지정돼있습니다.
| template <class T, class Allocator = allocator<T>> class vector; | cs |
이는 벡터를 인스턴스화 시킬때
이렇게 작성할 수도 있지만
| vector<int, MyAllocator> ints; | cs |
이렇게 작성할 수도 있습니다.
| template<typename A = int, typename B = double> class Bar { //... }; ... int main() { Bar<> bar; } | cs |
템플릿의 모든 매개변수가 기본 매개변수라면, 빈꺽쇠로 초기화 할 수 있습니다.
정보 : https://docs.microsoft.com/ko-kr/cpp/cpp/templates-cpp?view=vs-2017 ( Microsoft Docs 템플릿 ) https://docs.microsoft.com/ko-kr/cpp/cpp/template-specialization-cpp?view=vs-2017 ( Microsoft Docs 템플릿 특수화 ) http://tcpschool.com/cpp/cpp_template_class ( TCP School 템플릿 ) https://wikidocs.net/652 ( 함수 템플릿을 특수화 하지 말아야 하는 이유 )
|