2.1 클래스 템플릿 Stack의 구현
먼저 다음과 같이 Stack<>을 선언하고 정의하였습니다.
#include <vector>
#include <cassert>
template<typename T>
class Stack {
private:
std::vector<T> elems; // elements
public:
void push(T const& elem); // push element
void pop(); // pop element
T const& top() const; // return top element
bool empty() const { // return whether the stack is empty
return elems.empty();
}
};
template<typename T>
void Stack<T>::push (T const& elem)
{
elems.push_back(elem); // append copy of passed elem
}
template<typename T>
void Stack<T>::pop ()
{
assert(!elems.empty());
elems.pop_back(); // remove last element
}
template<typename T>
T const& Stack<T>::top () const
{
assert(!elems.empty());
return elems.back(); // return copy of last element
}
Stack<T>를 Stack으로 사용하는 것과 같이 클래스 템플릿 내에서 템플릿 인자가 뒤따르지 않는 클래스 이름은 자신의 인자로 템플릿 파라미터를 사용하는 클래스라는 뜻입니다.
2.2 클래스 템플릿 Stack의 사용
C++17 이전까지는 클래스 템플릿의 객체를 사용하려면 템플릿 인자를 명시해야 했습니다. 또한, 호출된 멤버 함수에 대한 코드만이 인스턴스화됩니다. 그리고 C++11부터는 꺾쇄 해킹(angle bracket hack)을 사용해 템플릿을 닫는 두 꺾쇠 사이에 공백을 둬야 한다는 규칙을 없앴습니다.
2.3 클래스 템플릿의 일부 사용
클래스 템플릿은 인스턴스화된 대상인 템플릿 인자에 대해 여러가지 연산을 적용하는데, 템플릿 인자는 실제로 사용되는(사용될 수도 있는이 아닌) 모든 필요 연산들만 제공하면 됩니다.
템플릿 라이브러리 내에서 계속 사용하는 제약 사항의 집합을 개념이라고 부르는데, 표준 라이브러리는 임의 접근 반복자 및 기본 생성 가능과 같은 개념을 사용합니다. 표준화 된 것은 존재하지만, 형식 특질을 통해 기본적인 제약 사항은 검사할 수 있습니다.
2.4 프렌드
프렌드 함수는 선언 뒤 정의하려면 문제가 복잡해지기에, 두 가지 방법으로 이를 해결할 수 있습니다.
- 클래스 템플릿 안에 새로운 함수 템플릿 선언
- 전방 선언 후 프렌드 선언
2.5 클래스 템플릿의 특수화
클래스 템플릿을 특정 템플릿 인자로 특수화할 수도 있습니다. 이는 template<>를 먼저 쓰고, 클래스 템플릿을 어떤 형식으로 특수화할 것인지 명시하여, 정의 가능합니다.
2.6 부분 특수화
클래스 템플릿은 부분적으로 특수화할 수 있습니다.
// class Stack<>의 포인터를 위한 부분 특수화:
template<typename T>
class Stack<T*> {
private:
std::vector<T*> elems; // elements
public:
void push(T*); // push element
T* pop(); // pop element
T* top() const; // return top element
bool empty() const { // return whether the stack is empty
return elems.empty();
}
};
또한, 여러 파라미터를 동시에 부분 특수화하는 것도 가능합니다.
// 두 템플릿 파라미터의 형식이 같음
template<typename T>
class Myclass<T, T>;
// 두 번째 형식이 int
template<typename T>
class Myclass<T, int>;
// 두 템플릿 파라미터의 형식이 모두 포인터형
template<typename T1, typename T2>
class Myclass<T1*, T2*>;
Myclass<int, int> m; // 모호함 발생
이런 경우에 두 부분 특수화가 겹쳐 모호한 경우가 발생할 수 있으므로, 이를 위해 부가적인 부분 특수화를 더 제공할 수도 있습니다.
2.7 기본 클래스 템플릿 인자
클래스 템플릿에서는 템플릿 파라미터의 기본값을 지정할 수 있습니다. 기본값을 지정할 때 이전 템플릿 파라미터를 참조할 수도 있습니다.
template<typename T, typename Cont = std::vector<T>>
class Stack {
...
};
2.8 형식 별칭
온전한 형식 자체에 새로운 이름을 부여하면 클래스 템플릿을 더 쉽게 사용할 수 있습니다. 이는 두 가지 방법이 존재합니다.
- typedef 키워드
- using 키워드(별칭 선언)
using 키워드가 C++11부터 등장하여 별칭 선언 문법이 더 읽기 수월하며 더 사용하기 좋습니다. 장점은 다음과 같습니다.
// 1. 별칭 템플릿을 이용 가능합니다.
template<typename T>
using DequeStack = Stack<T, std::deque<T>>;
// 2. 클래스 템플릿의 멤버인 형식을 줄여 표현할 때 유용합니다.
strcut MyType {
typdef ... iterator;
};
template<typename T>
using MyTypeIterator = typename MyType<T>::iterator;
// typedef 사용 시
typename MyType<T>::iterator pos;
// using 사용 시
MyTypeIterator<int> pos;
// 3. 형식 특질 접미사_t
// C++14를 기점으로, 형식 특질에 대한 줄임말을 제공합니다
// typedef 사용 시
typename std::add_cont<T>::type
// using 사용 시
std::add_cont_t<T>
2.9 템플릿 클래스 인자 연역
C++17 이전에는 클래스 템플릿을 사용할 때 모든 템플릿 파라미터 형식을 전부 나열해야만 했으나, C++17부터는 명시적으로 표기하지 않아도 됩니다. 함수 템플릿과 달리 클래스 템플릿 인자는 부분적으로 연역될 수 없습니다.
문자열 리터럴과 클래스 템플릿 인자 연역
문자열 리터럴로 스택을 초기화하는 경우에는 참조로 전달바든ㄴ 경우 파라미터의 형 소실이 일어나지 않아 포인터 형식이 아닌 배열 형식으로 들어오게 됩니다. 하지만 값으로 받는 경우는 앞서 얘기한듯 형이 소실되며 원시 포인터형으로 바뀌게 되기 때문에, 값으로 받는 것이 좋습니다.
연역 가이드
이미 있는 클래스 템플릿 인자 연역 방식에 새로운 방법을 추가하거나 수정하고 싶을 때 연역 가이드를 제공할 수 있습니다.
Stack(char const *) -> Stack<std::string>;
Stack stringStack1{"bottom"} // 잘 적용됨.
Stack stringStack2 = "bottom" // std::string을 기대하는 생성자에 문자열 리터럴을 전달해 복사 초기화 불가능.
2.10 템플릿화된 집합
집합(aggregate) 클래스(보통 사용하는 구조체)도 템플릿이 될 수 있습니다. 또한 C++17에서부터는 집합 클래스 템플릿에 대해서도 연역 가이드를 정의할 수 있습니다.
2.11 요약
- 클래스 템플릿은 하나 이상의 형식 파라미터가 정해지지 않은 채 구현된 클래스다
- 클래스 템플릿을 사용할 때 정해지지 않은 형식들에 대해 템플릿 인자를 꼭 전달해야 한다. 그 후 클래스 템플릿은 이들 형식에 대해 인스턴스화된다.
- 클래스 템플릿에서는 사용된 멤버 함수들만이 인스턴스화된다.
- 특정 형식에 대해 클래스 템플릿을 특수화할 수 있다. 특정 형식에 대해 클래스 템플릿을 부분적으로 특수화할 수 있다.
- C++17에서부터는 생성자에서부터 클래스 템플릿 인자를 자동으로 연역할 수 있다.
- 집합 클래스 템플릿도 정의할 수 있다.
- 템플릿 형식의 호출 파라미터는 값으로 전달될 때에만 형 소실된다.
- 템플릿은 전역/네임스페이스 영역 안이나 클래스 선언 안에서만 선언되고 정의될 수 있다.
'C++공부 > C++ Templates' 카테고리의 다른 글
6. 이동 의미 체계와 enable_if<> (0) | 2022.06.09 |
---|---|
5. 까다로운 기초 지식 (0) | 2022.06.09 |
4. 가변 인자 템플릿 (0) | 2022.06.09 |
3. 형식이 아닌 템플릿 파라미터 (0) | 2022.06.08 |
1. 함수 템플릿 (0) | 2022.06.08 |