Template Template Parameter

2022. 3. 25. 20:06C++

템플릿 템플릿 파라미터(Template Template Parameter)란, 템플릿 인자로 class template 또는 alias template을 전달할 수 있게하는 langauage feature이다.

 

템플릿 인자로 class template/alias template을 전달해서 어디에 쓰이는지 알아보기 위해 다음 코드를 보자.

template <typename T, typename Container>
class Stack
{
private:
    Container m_Container;

public:
    // You can use forwarding reference for the better performance
    void Push(T const& value);
    
    // Some member functions
    // ...
};

int main()
{
    // the element type 'int' is specified twice
    Stack<int, std::deque<int>> stack;
}

위 Stack 클래스 템플릿은 내부 구현에 사용될 컨테이너를 템플릿 파라미터(Container)로 받고있다.

하지만, main 함수 내에서 Stack 템플릿 클래스를 인스턴스화 할 때 요소(Element) 타입이 두 번 명시되는 것을 볼 수 있다. 멤버 함수의 파라미터로 사용되는 템플릿 파라미터 T와 컨테이너 요소 타입은 대부분 일치 할 것이기에, 이렇게 두 번 명시하는 경우를 피하고 싶을 것이다.

 

이럴 때 템플릿 템플릿 파라미터를 사용하여 Container 템플릿 인자로 요소가 명시된 템플릿 클래스 대신에 클래스 템플릿을 전달한다. 다음 코드를 보자.

template <typename T, 
    template <typename> 
    class Container = std::deque>
class Stack
{
private:
    Container<T> m_Container;

public:
    // You can use forwarding reference for the better performance
    void Push(T const& value);
    
    // Some member functions
    // ...
};

int main()
{
    Stack<int, std::deque> stack;
}

template <typename T, template <typename> ..> 이 부분을 보면 왜 템플릿 템플릿 파라미터인지 알 수 있을 것이다.

이제 Stack<int, std::deque>와 같이 요소 타입을 한 번만 명시해도 되며, 클래스 템플릿을 인자로 전달한다.

 

위 코드를 Clang 컴파일러에서 실행하면 다음과 같은 컴파일 에러가 뜬다.

(MSVC/GCC에서는 정상적으로 컴파일 된다.)

error: template template argument has different template parameters than its corresponding template template parameter

템플릿 템플릿 인자 std::deque가 대응되는 템플릿 템플릿 파라미터 Container와 다른 템플릿 파라미터들을 가지고 있다고 말하고 있다.

 

이 문제는 std::deque가 요소 타입 외 Allocator라는 템플릿 파라미터를 가지고 있기 때문에 발생한다.

Allocator 템플릿 파라미터는 템플릿 인자가 디폴트로 할당되어 있는데, 따라서 MSVC/GCC 컴파일러는 이 인자에 대해 신경쓰지 않아도 되는 반면에, Clang은 아직 지원되지 않는 듯 하다.

 

해결 방법은 아래 코드와 같이 2가지가 있다.

template <typename T, 
    template <typename Elem, typename = std::allocator<Elem>> 
    class Container = std::deque>
template <typename T, 
    template <typename...> 
    class Container = std::deque>

 

참고로 C++17부터는 템플릿 템플릿 파라미터를 선언할 때 typename 또는 class 둘 중 어느 것을 사용해도 상관없다.

(C++14에서도 MSVC/Clang/GCC 모두 Compiler Explorer에서 작동하긴 하는데, Clang/GCC에서는 typename을 사용했을 때 C++17 확장 기능이라고 경고를 준다.) 

template <typename T, 
    template <typename> 
    class Container = std::deque> // OK
template <typename T, 
    template <typename> 
    typename Container = std::deque> // OK

 

[참고]

- C++ Templates: The Complete Guide, 2nd Edition