- 최초에 상정된 표준 C++ 라이브러리의 주요 구성요소는 STL, iostream, 로케일 등이다. 여기에는 C89의 표준 라이브러리도 포함되어 있다.
- TR1이 도입되면서 추가된 것은 스마트 포인터, 일반화 함수 포인터(tr1::function), 해시 기반 컨테이너, 정규 표현식 그 외의 10개 구성요소이다.
- TR1 자체는 단순히 명세서이다. TR1의 기능을 사용하기 위해서는 명세를 구현한 코드를 구해야 한다. TR1 구현을 구할 수 있는 자료처 중 한 군데가 바로 부스트이다.
TR1을 포함한 표준 라이브러리
C++ 표준에는 계속해서 새로운 기능이 추가되어왔다.
TR1은 C++ 라이브러리 작업 그룹이 작성한 1차 기술 보고서이다.
TR1이 포함된 것을 C++ 표준 1.1이라고 부른다.
TR1을 비롯한 C++ 라이브러리의 주요 구성요소를 알아보자.
표준 C++ 라이브러리
- 표준 템플릿 라이브러리(Standard Template Library: STL)
주요 구성요소로서 컨테이너(vector, string, map 등), 반복자, 알고리즘(find, sort, transform), 함수 객체(less, greater 등) 외에 컨테이너 어댑터와 함수 객체 어댑터(stack, priority_queue, mem_fun, not1 등)가 있다.
- iostream
사용자 정의 버퍼링, 국제화 기능이 가능한 입출력을 지원하며, 그 외에 cin, cout, cerr, clog 등의 사전정의 객체를 지원한다.
- 국제화 지원
여러 로케일(locale)을 활성화시킬 수 있는 기능이 포함되어 있다. 또한 wchar_t 등의 타입 및 wstring을 쓰면 유니코드를 사용할 수 있다.
- 수치 처리 지원
복소수를 나타내는 템플릿 및 수치 배열을 나타내는 템플릿(valarray)이 여기에 해당된다.
- 예외 클래스 계통
최상위 클래스인 exception 및 이것으로부터 파생된 클래스(logic_error, runtime_error 등)가 포함된다.
- C89의 표준 라이브러리
1989년 버전의 C에 포함된 표준 라이브러리는 전부 C++에도 들어 있다.
TR1
TR1을 통해 명시된 새로운 구성요소는 총 14개이다.
14개 모두 std 네임스페이스에 들어 있는데, 더 정확히 말하면 std 안에 중첩된 tr1이란 네임스페이스에 들어 있다.
TR1 구성요소를 정리해 보자.
- 스마트 포인터(smart pointer)
tr1::shared_ptr, tr1::weak_ptr이 여기에 해당한다.
동작은 기본제공 포인터와 똑같으나, 하나의 실제 객체를 가리키는 자신과 같은 포인터의 개수를 유지해 놓는 포인터이다.
이런 기법을 가리켜 참조 카운팅이라고 부른다.
어떤 객체를 가리키는 최후의 스마트 포인터가 소멸될 때, 그 객체도 자동으로 삭제된다.
스마트 포인터는 비순환형 자료구조 내의 자원 누출을 방지하는 용도에 적합한데, 만약 두 개 이상의 객체가 순환구조를 이루고 있으면, 이 순환 구조 때문에 각 객체의 참조 카운트가 0이 안될 수가 있다.
순환 구조를 가리키는 외부 포인터가 소멸된 후에도 유지될 수 있다.
이런 상황을 막기 위해 쓰는 것이 tr1::weak_ptr이다.
tr1::weak_ptr은 tr1::shared_ptr에 기반한 비순환형 자료구조에서 순환고리를 가능하게 하는 포인터로 동작하게끔 설계되어 있다.
어떤 객체를 물고 있는 마지막 tr1::shared_ptr이 소멸될 때, tr1::weak_ptr이 여전히 계속해서 그 객체를 가리키고 있더라도 그 객체는 삭제된다.
단, 이 경우 weak_ptr은 무효상태로 표시된다.
- tr1::function
어떤 함수가 가진 시그니처와 호환되는 시그니처를 갖는 함수호출성 개체의 표현을 가능하게 해 주는 템플릿이다.
시그니처가 비슷하면 호출이 가능한 일반화 콜백 함수를 만들어 보자는 것이 주요 개념이다.
int를 받고 string을 반환하는 콜백 함수를 등록하도록 만들고 싶으면 다음과 같이 하면 된다.
void registerCallback(std::string func(int)); // int를 받고 string을 반환하는 함수가 매개변수
매개변수 이름으로 쓰인 func는 사실 없어도 되는 선택사항이므로, registerCallback은 다음과 같이 선언해도 문제없다.
void registerCallback(std::string (int));
여기서 매개변수 타입에 해당하는 std::string(int) 부분이 바로 함수 시그니처이다.
바로 이런 용도에 tr1::function 템플릿을 사용하면 registerCallback이 훨씬 융통성 있게 만들어진다.
함수 시그니처 대신에 임의의 함수호출성 개체를 받도록 만들게 함으로써, int 타입 혹은 int로 변환이 가능한 어떤 타입도 전달받으며, string 타입 혹은 string으로 변환이 가능한 어떤 타입도 반환할 수 있는 그런 함수를 registerCallback의 매개변수로 설정할 수 있다.
tr1::function의 템플릿 매개변수로는 앞의 그 시그니처를 넘긴다.
void registerCallback(std::tr1::function<std::string(int)> func);
- tr1::bind
STL 바인더로 쓰이고 있는 bind1st 및 bind2nd와 같이 동작함은 물론, 그보다 훨씬 더 많은 기능이 있는 범용 바인더이다.
TR1 이전의 바인더들과 달리, tr1::bind는 상수 멤버 함수 및 비상수 멤버 함수에 상관없이 동작한다.
또한 참조로 전달되는 매개변수에 대해서도 동작한다.
마지막으로, 외부 보조 없이도 함수 포인터를 자체적으로 다룰 수 있기 때문에, tr1::bind를 호출하기 전에 ptr_fun, mem_fun, mem_fun_ref를 넣을 필요가 없다.
- 해시 테이블(hash table)
세트, 멀티세트, 맵, 멀티맵을 구현하는 데 이 해시 테이블이 쓰였다.
각각의 인터페이스는 똑같은 이름을 가진 TR1 이전의 연관 컨테이너의 인터페이스를 본떠서 만들어졌다.
tr1::unordered_set, tr1::unordered_multiset, tr1::unordered_map, tr1::unordered_multimap 등이 있다.
set, multiset, map, multimap에 저장되는 원소와 달리, TR1의 해시 기반 컨테이너는 원소가 저장되는 순서를 예측할 수 없다는 점을 강조한다.
- 정규 표현식(regular expression)
정규 표현식 기반의 탐색과 문자열에 대한 대체 연산이 가능하며, 일치되는 원소들 사이의 순회도 지원한다.
- 튜플(tuple)
표준 라이브러리에 원래 있었던 pair 템플릿 버전이다.
pair객체의 경우에는 두 개만 담을 수 있는 반면, tr1::tuple 객체는 몇 개라도 담을 수 있다.
- tr1::array
begin과 end 등의 멤버 함수를 지원하는 배열이다.
tr1::array 객체의 크기는 컴파일 과정에서 정해진다.
즉, 동적 메모리를 사용하지 않는다.
- tr1::mem_fn
C++98의 mem_fun 및 mem_fun_ref를 확장시키는 템플릿이다.
- tr1::reference_wrapper
기존의 참조자가 객체처럼 행동할 수 있도록 만들어주는 템플릿이다.
이것을 사용하면 참조자를 담은 것처럼 동작하는 컨테이너를 만들 수 있다는 점이 가장 크다.
- 난수 발생
C++가 C 표준 라이브러리로부터 물려받은 rand 함수보다 우수한 난수 발생 기능이다.
- 특수 용도의 수학 함수
라게르 다항식, 베셀 함수, 완전 타원 적분 등 많은 기능이 있다.
- C99 호환성 확장 기능
C99의 새로운 라이브러리를 C++로 가져올 목적으로 설계된 함수 및 템플릿 모음
- 타입 특성정보(type traits)
주어진 타입에 대한 컴파일 타임 정보를 제공하는 특성정보 클래스의 모음이다.
어떤 T라는 타입에 대해 TR1의 타입 특성정보 기능을 적용하면, T가 기본제공 타입인지, 가상 소멸자를 지원하는지, 공백 클래스인지, 다른 U타입으로 암시적 변환이 가능한지 등의 정보를 알아낼 수 있다.
심지어, 주어진 타입의 적절한 바이트 정렬까지 잡아내준다.
- tr1::result_of
어떤 함수 호출의 반환 타입을 추론해 주는 템플릿이다.
템플릿을 만들 때 어떤 함수의 호출로 인해 반환되는 객체의 타입을 참조하고 싶은 상황이 있을 수 있다.
하지만, 반환 타입은 그 함수의 매개변수 타입에 따라 달라질 수 있다.
tr1::result_of 템플릿을 쓰면 함수 반환 타입을 쉽게 참조할 수 있다.
TR1 구성요소들 중에는 TR1 이전의 구성요소들 몇 개에 대해서 기능을 그대로 끌어온 것들이 있긴 하지만, 현재의 TR1은 표준 라이브러리에 대한 부가 구성요소이다.
기존의 구성요소 대신에 TR1이 쓰일 수 있다는 이야기가 아니다.
부스트
TR1 자체는 그냥 문서일 뿐이다.
이 문서에 명시된 기능을 사용하려면, 내용을 실제로 구현한 코드를 구해야 한다.
그중 하나가 부스트이다.
하지만, 부스트의 구성요소가 TR1 명세와 맞지 않는 부분이 있긴 하다.
부스트 라이브러리에 속한 모든 구성요소는 boost 네임스페이스에 들어 있지만, TR1 구성요소는 std::tr1에 들어 있어야 하는 것이 규칙이다.
따라서 컴파일러에게 std::tr1에 대한 참조를 boost에 대한 참조로 처리해 달라고 하면 된다.
namespace std {
namespace tr1 = ::boost; // std::tr1 네임스페이스는 이제 boost 별칭으로
}
기술적으로 볼 때, 이러한 꼼수를 부렸을 때 결과는 정의되어 있지 않다.
std 네임스페이스에는 사용자 수준에서 어떤 것도 추가할 권한이 없기 때문이다.
하지만, 실제로 문제가 안될 수도 있다.
사용하는 컴파일러가 자체적으로 TR1 구현을 제공한다면, 위에 있는 별칭을 없애면 된다.