본 프로젝트의 개요와 프로젝트를 진행하기 위한 개념 확립
템플릿을 사용하면 객체를 생성하지 않더라도 타입에 대한 어떠한 값을 부여할 수 있고, 또 그 타입들을 가지고 연산을 할 수가 있다. 또한 타입은 반드시 컴파일 타임에 확정되어야 하므로, 컴파일 타임에 모든 연산이 끝나게 된다. 이렇게 타입만 가지고 컴파일 타임에 생성되는 코드로 프로그래밍하는 것을 메타 프로그래밍이라고 하고, C++의 경우에는 템플릿을 가지고 이러한 작업을 하기 때문에 템플릿 메타 프로그래밍이라고 부른다.
Algorithm
, Container
, Functor
, Iterator
라고 불리는 네 가지의 구성 요소를 제공한다.
vector (Mandatory Part)
deque
list
stack (Mandatory Part)
queue
priority_queue
map (Mandatory Part)
multimap
set (Bonus Part)
multiset
bitset
pair : 간단한 연관 컨테이너로서 데이터 요소의 2-tuple 또는 객체들로 이루어짐 (클래스 템플릿)
make_pair : 두 개의 인자를 받아 pair 객체를 생성 (함수 템플릿)
input iterator
output iterator
forward iterator
bidirectional iterator
random access iterator
즉, iter += dist
는 random access iterator 아니면 적용이 불가능하다. 또한 후진이 불가능한 iterator에 대해 음수의 dist가 넘어올 때 적절한 예외처리가 필요하다.
Traits class란 컴파일 도중에 어떤 주어진 타입의 정보를 얻을 수 있게 하는 객체이다. 템플릿 클래스나 함수에서 여러 타입에 대해 각기 다르게 처리를 해줘야 하는 상황에서 컴파일 타임에 타입들을 구분하기 위해 사용하는 것이다.
Iterator traits 클래스는 타입을 구분할 수 있는 tag 구조체를 활용한다. 이를 반복자 범주를 두 부분으로 나누어 구현한다.
사용자 정의 타입으로 하여금 iterator_category
라는 이름의 타입을 내부에 가질 것을 요구한다.
예를 들어, list의 경우 bidirectional_iterator_tag
를 iterator_category
로 typedef
한다.
사용자가 정의한 타입을 똑같이 가진 iterator_traits
구조체를 정의한다. 또한 struct
iterator_traits
기본 제공 타입에 대해서도 특성 정보를 사용할 수 있도록 부분 특수화 버전을 만들어준다.
각 iterator
를 사용하는 곳에서 iterator_category
를 typedef
하고 있다. 또한 iterator_traits
구조체는 넘어온 iterator::iterator_category
타입을 자신의 iterator_category
로 typedef
하고 있다.
equal : 두 원소의 나열들이 일치하는지 확인
lexicographical_compare : 사전적 크기 비교 수행
C++11 표준의 14.8.2 조항에 따르면 템플릿 인자 치환에 실패할 경우 컴파일러는 이 오류를 무시하고 그냥 오버로딩 후보에서 제외하면 할 수 있다. C++에서는 이를 SFINAE - Substitution Failure Is Not An Error(치환 실패는 오류가 아니다)라고 한다.
SFINAE를 잘 활용하는 툴 중 가장 널리 쓰이는 것이 바로 enable_if
이다. enable_if
는 템플릿들을 위한 컴파일 타임 스위치이며 이게 없으면 오버로딩 함수들이 무분별하게 오버로딩 될 수 있다.
예제
template <typename T>
class vector
{
vector(size_type n, const T val);
template <class InputIterator>
vector(InputIterator first, InputIterator last);
};
위와 같이 두 가지 형태의 두 개의 인자를 받는 생성자가 있고 std::vector<int> v(2, 4);
로 vector를 생성했을 때, 프로그래머는 첫 번째 생성자를 의도한 것이겠지만 실제로는 두 번째 생성자가 호출된다. 왜냐하면 size_type
이 대개 unsigned
로 있지만, 2의 경우 그냥 sign
이므로 더 잘 맞는 후보군은 두 번째 것이 되기 때문이다. 따라서 enable_if
를 이용해서 InputIterator
가 정말로 반복자일 때만 오버로딩 될 수 있도록 제한해야 한다.
STL allocator를 제작할 때, 표준 컨테이너에서 필요로 하는 rebind라는 중첩 구조체 템플릿을 반드시 제공해야 한다. 예를 들어, set은 단순히 <T>
를 저장하는 컨테이너가 아니라 <T>
타입 데이터를 포함한 노드 구조체를 저장하는 컨테이너이다.
하지만 allocator에 넘긴 템플릿 매개변수는 <T>
인데 이것만으로 allocator 함수를 호출한다면 딸랑 <T>
의 크기만큼의 바이트만 할당이 될 것이다.
이렇듯 해당 템플릿 매개변수 <T>
타입의 컨테이너 allocator는 <T>
타입이 아닌 해당 컨테이너에서 요구되는 진정한 할당 타입의 allocator가 필요하다.
이와 같은 요구사항을 충족하기 위해 STL allocator는 반드시 rebind 중첩 구조체 템플릿을 가져야 한다.
References
https://www.cplusplus.com/reference/
https://modoocode.com/255
https://modoocode.com/174
https://modoocode.com/221
http://egloos.zum.com/sweeper/v/3007176
http://egloos.zum.com/sweeper/v/2966785
https://ko.wikipedia.org/wiki/%ED%91%9C%EC%A4%80_%ED%85%9C%ED%94%8C%EB%A6%BF_%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC