클래스 템플릿(Class template)

클래스 템플릿(Class template)

비슷한 클래스를 타입만 바꿔가며 여러 개 만들어야 할 때, 하나의 "틀"을 만들어 두고 타입을 넣어 찍어내면 편해요. **클래스 템플릿(class template)**이 바로 그런 방법이에요. 클래스 템플릿은 클래스들의 한 가족(family of classes)을 정의해요.

출처: cppreference

본문

문법

template < parameter-list > class-declaration (1)
template < parameter-list > requires constraint class-declaration (2) (C++20부터)
export template < parameter-list > class-declaration (3) (C++11에서 제거)
자리 의미
class-declaration 클래스 선언. 선언된 클래스 이름이 템플릿 이름이 돼요.
parameter-list 템플릿 매개변수들의 비어 있지 않은 쉼표 구분 목록. 각각은 비타입 매개변수, 타입 매개변수, 템플릿 매개변수, 또는 그중 어떤 것의 매개변수 팩이에요.
constraint 이 클래스 템플릿이 받아들이는 템플릿 매개변수를 제한하는 제약 표현식

export는 템플릿을 exported(내보내짐)로 선언하는 선택적 수식자였어요(클래스 템플릿에 쓰면 그 모든 멤버도 내보내진 것으로 선언됐어요). 내보내진 템플릿을 인스턴스화하는 파일은 그 정의를 포함할 필요가 없었어요. 선언만으로 충분했거든요. export의 구현은 드물었고 세부 사항에서 서로 일치하지 않았어요. (C++11 이전)

클래스 템플릿 인스턴스화(Class template instantiation)

클래스 템플릿 자체는 타입도, 객체도, 다른 어떤 실체도 아니에요. 템플릿 정의만 담긴 소스 파일에서는 어떤 코드도 생성되지 않아요. 코드가 나타나려면 템플릿이 **인스턴스화(instantiated)**되어야 해요. 컴파일러가 실제 클래스(또는 함수 템플릿이면 함수)를 생성할 수 있도록 템플릿 인자가 제공되어야 하는 거예요.

명시적 인스턴스화(Explicit instantiation)

template class-key template-name < argument-list > ; (1)
extern template class-key template-name < argument-list > ; (2) (C++11부터)
자리 의미
class-key class, struct, union 중 하나

명시적 인스턴스화 정의(definition)는 그들이 가리키는 클래스, 구조체, 공용체의 인스턴스화를 강제해요. 프로그램의 템플릿 정의 뒤 어디에나 나타날 수 있고, 주어진 argument-list에 대해 전체 프로그램에서 한 번만 나타날 수 있어요(진단이 요구되진 않아요).

명시적 인스턴스화 선언(extern template)은 암시적 인스턴스화 단계를 건너뛰어요. 암시적 인스턴스화를 일으킬 코드 대신, 다른 곳에 제공된 명시적 인스턴스화 정의를 사용해요(그런 인스턴스화가 없으면 링크 오류가 나요). 이것으로 컴파일 시간을 줄일 수 있어요. 템플릿을 쓰는 소스 파일들 중 단 하나만 빼고 모두에 템플릿 인스턴스화를 명시적으로 선언하고, 남은 파일에서 명시적으로 정의하는 거예요. (C++11부터)

클래스, 함수, 변수(C++14부터), 멤버 템플릿 특수화는 템플릿으로부터 명시적으로 인스턴스화될 수 있어요. 클래스 템플릿의 멤버 함수, 멤버 클래스, 정적 데이터 멤버는 그 멤버 정의로부터 명시적으로 인스턴스화될 수 있어요.

명시적 인스턴스화는 qualified-id를 사용하지 않는 한 템플릿의 둘러싸는 이름공간에만 나타날 수 있어요.

namespace N
{
    template<class T>
    class Y // 템플릿 정의
    {
        void mf() {}
    };
}

// template class Y<int>; // error: 클래스 템플릿 Y가 전역 이름공간에 보이지 않음
using N::Y;
// template class Y<int>; // error: 템플릿의 이름공간 밖에서의 명시적 인스턴스화
template class N::Y<char*>;       // OK: 명시적 인스턴스화
template void N::Y<double>::mf(); // OK: 명시적 인스턴스화

같은 템플릿 인자 집합에 대해 명시적 특수화가 먼저 나타났다면, 명시적 인스턴스화는 효과가 없어요.

함수 템플릿, 변수 템플릿(C++14부터), 클래스 템플릿의 멤버 함수나 정적 데이터 멤버, 멤버 함수 템플릿을 명시적으로 인스턴스화할 때는 선언만 보이면 돼요. 클래스 템플릿, 클래스 템플릿의 멤버 클래스, 멤버 클래스 템플릿을 명시적으로 인스턴스화하려면, 같은 템플릿 인자를 가진 명시적 특수화가 먼저 나타나지 않았다면 완전한 정의가 먼저 와야 해요.

함수 템플릿, 변수 템플릿(C++14부터), 멤버 함수 템플릿, 또는 클래스 템플릿의 멤버 함수나 정적 데이터 멤버를 명시적 인스턴스화 정의로 명시적으로 인스턴스화하면, 그 템플릿 정의가 같은 번역 단위 안에 있어야 해요.

명시적 인스턴스화가 클래스 템플릿 특수화를 이름으로 가리키면, 그것은 그 특수화의 각 비상속·비템플릿 멤버 중 이전에 그 번역 단위에서 명시적으로 특수화되지 않은 것들에 대한 같은 종류(선언 또는 정의)의 명시적 인스턴스화가 돼요. 이 명시적 인스턴스화가 정의라면, 그 시점에서 정의된 멤버들에 대해서만 명시적 인스턴스화 정의이기도 해요.

명시적 인스턴스화 정의는 멤버 접근 지정자를 무시해요. 즉, 매개변수 타입과 반환 타입이 private일 수 있어요.

암시적 인스턴스화(Implicit instantiation)

코드가 타입을 완전히 정의해야 하는 문맥에서 템플릿을 참조하거나, 타입의 완전성이 코드에 영향을 미치는데 그 특정 타입이 명시적으로 인스턴스화되지 않았다면, **암시적 인스턴스화(implicit instantiation)**가 일어나요. 예를 들어 이 타입의 객체를 생성할 때는 일어나지만, 이 타입에 대한 포인터를 생성할 때는 일어나지 않아요.

이것은 클래스 템플릿의 멤버에도 적용돼요. 멤버가 프로그램에서 사용되지 않으면 인스턴스화되지 않고, 정의도 요구되지 않아요.

template<class T>
struct Z // 템플릿 정의
{
    void f() {}
    void g(); // 정의되지 않음
};

template struct Z<double>; // Z<double>의 명시적 인스턴스화
Z<int> a;                  // Z<int>의 암시적 인스턴스화
Z<char>* p;                // 여기서는 아무것도 인스턴스화되지 않음

p->f(); // 여기서 Z<char>와 Z<char>::f()의 암시적 인스턴스화가 일어남
        // Z<char>::g()는 결코 필요하지 않고 인스턴스화되지 않음:
        // 정의되어 있지 않아도 됨

클래스 템플릿이 인스턴스화 시점에 선언됐지만 정의되지 않았다면, 인스턴스화는 불완전 클래스 타입을 만들어 내요.

template<class T>
class X;    // 선언, 정의는 아님

X<char> ch; // error: 불완전 타입 X<char>

지역 클래스와 그 멤버에 사용된 템플릿들은, 그 지역 클래스나 열거형이 선언된 실체의 인스턴스화의 일부로 인스턴스화돼요. (C++17부터)

더 알아보기

  • 템플릿 매개변수와 인자: 템플릿을 매개변수화하는 방법.
  • 함수 템플릿 선언: 함수의 가족을 정의하는 방법.
  • 템플릿 특수화: 특정 타입에 대해 기존 템플릿을 정의.
  • 매개변수 팩(parameter packs): 템플릿에서 타입 목록을 사용하는 방법 (C++11부터).