C++0x 미리보기 3, 템플릿 별칭 (1부)

Twitter icon류광, 2007-12-04 21:12
많은 C++ 프로그래머가 원하던 소위 "템플릿 typedef", 그러나 typedef가 아닌 다른 키워드가 쓰입니다.

이 제안의 필요성을 이해하기에는 관련 제안 문서들보다는 Herb Sutter의 Gotw #79: Template Typedef에 있는 예제가 더 적합할 것 같습니다. 이 글 전반부의 예제 코드는 그 글에서 가져온 것입니다.

문자열을 키로 하는 맵은 흔히 쓰이는 자료구조입니다. 최종 사용자용 응용프로그램 또는 그런 응용프로그램을 위한 서비스나 라이브러리에서는 맵의 키가 문자열인 경우가 많습니다. 그런 경우 키의 형식은 std::string으로 같되 값의 형식만 다른 여러 형식들이 반복되는 코드가 필요하게 됩니다. 예를 들면:

std::map<std::string, Employee> employeeRoster;
std::map<std::string, void (*)(int)> callbacks;
std::map<std::string, ClassFactory*> factories;

명백한 중복이 보이는 코드입니다. 대신 이런 코드가 훨씬 낫습니다.

// 예제 1
Registry<Employee> employeeRoster;
Registry<void (*)(int)> callbacks;
Registry<ClassFactory*> factories;

이런 코드가 컴파일되게 하려면 컴파일러에게 Registry<?>라는 것이 std::map<std::string, ?>임을 알려 주어야 합니다. 그런데 현재의 C++ 표준에서는 그렇게 하기가 의외로 복잡합니다. 두 가지 방법이 있지만 둘 다 만족스럽지 않습니다.

하나는 Registry를 std::map을 상속하는 클래스 템플릿으로 정의하는 것이고, 또 하나는 Registry를 std::map 멤버를 포함하는 클래스 템플릿으로 정의하는 것입니다. 그런데 STL의 컨테이너들은 상속을 염두에 두고 만들어진 것이 아니라서 여러 가지 문제가 발생할 수 있습니다. 그리고 std::map을 멤버로 포함하는 경우에는 std::map의 모든 메서드마다 그에 해당하는 전달 메서드들을 일일이 정의해 주어야 합니다. 두 방법 모두 배보다 배꼽이 더 큰 해법이라고 할 수 있습니다.

한편, 예제 1의 간결함을 조금 포기하는 대신 Registry를 좀 더 쉽게 정의할 수 있는 대안도 있습니다. 구체적으로 말하면 Registry를 다음과 같이 정의하고,

template<class T>
struct Registry
{
    typedef std::map<std::string, T> Type;
};

사용 코드에는 ::Type을 덧붙이는 것입니다.

Registry<Employee>::Type employeeRoster;
Registry<void (*)(int)>::Type callbacks;
Registry<ClassFactory*>::Type factories;

여러 템플릿 라이브러리들에서 사용하고 있는 일종의 관용구에 해당하는 방법으로, 표준의 '템플릿 typedef 불가'에 대해 C++ 커뮤니티가 발견(또는 발명)한 사실상 표준적인 기법이라고 할 수 있습니다(그런만큼 이 제안과는 별도로 이런 관용구를 기억해 두는 것도 좋을 것입니다). 그리고 이 해답이 충분히 효과적이므로 C++에 굳이 새로운 구문을 추가할 필요는 없다는 주장도 있습니다. 그러나 이것이 최선이 아님은 분명하기 때문에, 표준 위원회는 템플릿에 대한 별칭을 언어 자체에서 지원하는 구문을 C++ 표준에 추가하기로 했습니다.

그렇다면 어떤 구문이 필요할까요? 목표는 예제 1의 코드가 그대로 컴파일되도록 Registry를 정의하되, "Registry<?>는 std::map<std::string, ?>이다"라는 문장 만큼이나 간단하게 정의할 수 있어야 한다는 것입니다. 아마도 가장 자연스럽게 떠올릴 수 있는 구문은 다음과 같은 형태일 것입니다.

// 예제 2
template<class T>
typedef std::map<std::string, T> Registry;

이 구문은 다음과 같은 이유로 자연스럽습니다.

  1. 첫째, 일정한 형식 매개변수를 사용하는 일반화된(매개변수화된) 형식을 만들고자 하는 것이므로 당연히 template로 시작합니다.
  2. 둘째, 한 형식에 대한 새로운 이름을 붙이는 것이므로 typedef 키워드를 사용하는 것이 당연합니다.

그러나 제안자들은 이 구문을 선택하지 않았습니다. 사실 위의 두 항목을 보면 모순이 있습니다. 첫 항목에서는 ‘일반화된 형식’을 만들고자 한다고 했지만, 둘째 항목에서는 ‘일반화된’이 슬쩍 빠져 있습니다. typedef는 (구체적인)형식에 대한 별칭을 만드는 수단이고 지금 필요한 것은 일반화된 형식에 대한 별칭을 만드는 것이므로 typedef를 사용하는 것은 혼란을 야기한다는 것이 제안자들의 판단이었습니다. Registry는 형식에 대한 별칭이 아니므로 typedef를 적용하면 안 된다는 것이죠. 이 제안의 이름이 Templates typedef for C++이 아니라 Templates aliases for C++인 것도 그러한 이유입니다.

슬슬 결론을 내리자면, 제안자들은 typedef 대신 using 키워드를 선택했습니다. 제안에 따라 Registry를 정의하면 다음과 같습니다.

template<class T>
  using Registry = std::map<std::string, T>;

이런 구문이 typedef 구문에 비해 어색하게 느껴질 수도 있겠지만, 이런 구문을 제안하게 된 데에는 위에서 말한 typedef의 문제점 외에도 다른 이유가 있습니다. 그런데 글이 생각보다 길어졌네요. using 구문의 장점은 다음 편으로 미루겠습니다.

태그: 프로그래밍 C++ C++0x

comments powered by Disqus

예전 댓글(읽기 전용)