Namespaces
Variants

Partial template specialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

주어진 범주의 템플릿 인수에 대해 클래스 및 변수 (C++14 이후) 템플릿을 사용자 정의할 수 있습니다.

목차

구문

template < 매개변수-목록 > 클래스-키 클래스-헤드-이름 < 인수-목록 > 선언 (1)
template < 매개변수-목록 > 선언-지정자-시퀀스 선언자 < 인수-목록 > 초기화자  (선택 사항) (2) (C++14부터)

여기서 class-head-name 은 이전에 선언된 class template 의 이름을 나타내고, declarator 는 이전에 선언된 variable template 의 이름을 나타냅니다 (C++14부터) .

부분 전문화는 해당 주 템플릿이 정의될 수 있는 모든 범위에서 선언될 수 있습니다 (이는 주 템플릿이 정의된 범위와 다를 수 있습니다. 예를 들어 멤버 템플릿 의 클래스 외부 특수화와 같은 경우). 부분 전문화는 비특수화된 템플릿 선언 이후에 나타나야 합니다.

예를 들어,

template<class T1, class T2, int I>
class A {};             // 기본 템플릿
template<class T, int I>
class A<T, T*, I> {};   // #1: T2가 T1에 대한 포인터인 부분 특수화
template<class T, class T2, int I>
class A<T*, T2, I> {};  // #2: T1이 포인터인 부분 특수화
template<class T>
class A<int, T*, 5> {}; // #3: T1이 int이고, I가 5이며, T2가 포인터인
                        //     부분 특수화
template<class X, class T, int I>
class A<X, T*, I> {};   // #4: T2가 포인터인 부분 특수화

표준 라이브러리의 부분 특수화 예시로는 배열 타입에 대한 부분 특수화를 갖는 std::unique_ptr 이 있습니다.

인수 목록

다음 제한 사항들이 부분 템플릿 특수화의 argument-list 에 적용됩니다:

1) 인수 목록이 비특수화된 인수 목록과 동일할 수 없습니다(무언가를 특수화해야 함):
template<class T1, class T2, int I> class B {};        // primary template
template<class X, class Y, int N> class B<X, Y, N> {}; // error

또한 특수화는 기본 템플릿보다 더 특수화되어야 합니다

template<int N, typename T1, typename... Ts> struct B;
template<typename... Ts> struct B<0, Ts...> {}; // Error: not more specialized
(C++11부터)
2) 기본 인수는 인수 목록에 나타날 수 없습니다
3) 어떤 인수가 pack expansion인 경우, 반드시 목록의 마지막 인수여야 합니다
4) 템플릿 매개변수가 비추론 문맥 외부에서 최소 한 번 이상 나타나는 한, 상수 인수 표현식은 템플릿 매개변수를 사용할 수 있습니다 (현재 clang과 gcc 12만 이 기능을 지원함):
template<int I, int J> struct A {};
template<int I> struct A<I + 5, I * 2> {}; // error, I is not deducible
template<int I, int J, int K> struct B {};
template<int I> struct B<I, I * 2, 2> {};  // OK: first parameter is deducible
5) 상수 템플릿 인자는 특수화의 매개변수에 의존하는 타입을 가진 템플릿 매개변수를 특수화할 수 없습니다:
template<class T, T t> struct C {}; // primary template
template<class T> struct C<T, 1>;   // error: type of the argument 1 is T,
                                    // which depends on the parameter T
template<int X, int (*array_ptr)[X]> class B {}; // primary template
int array[5];
template<int X> class B<X, &array> {}; // error: type of the argument &array is
                                       // int(*)[X], which depends on the parameter X

이름 검색

부분 템플릿 특수화는 이름 검색으로 찾을 수 없습니다. 오직 기본 템플릿이 이름 검색으로 발견될 때만 해당 부분 특수화들이 고려됩니다. 특히, 기본 템플릿을 가시적으로 만드는 using 선언은 부분 특수화들도 함께 가시적으로 만듭니다:

namespace N
{
    template<class T1, class T2> class Z {}; // 기본 템플릿
}
using N::Z; // 기본 템플릿을 참조함
namespace N
{
    template<class T> class Z<T, T*> {};     // 부분 특수화
}
Z<int, int*> z; // 이름 검색에서 N::Z(기본 템플릿)를 찾은 후,
                // T = int인 부분 특수화가 사용됨

부분 순서

클래스 또는 변수 (since C++14) 템플릿이 인스턴스화될 때, 부분 특수화가 사용 가능한 경우 컴파일러는 기본 템플릿을 사용할지 아니면 그 부분 특수화 중 하나를 사용할지 결정해야 합니다.

1) 템플릿 인자와 일치하는 특수화가 하나만 있는 경우, 해당 특수화가 사용됩니다
2) 둘 이상의 특수화가 일치하는 경우, 부분 순서 규칙을 사용하여 어떤 특수화가 더 특수화되었는지 결정합니다. 가장 특수화된 특수화가 사용되며, 이것이 고유한 경우에 한합니다(고유하지 않으면 프로그램을 컴파일할 수 없음)
3) 특수화가 일치하지 않으면 기본 템플릿이 사용됨
// given the template A as defined above
A<int, int, 1> a1;   // no specializations match, uses primary template
A<int, int*, 1> a2;  // uses partial specialization #1 (T = int, I = 1)
A<int, char*, 5> a3; // uses partial specialization #3, (T = char)
A<int, char*, 1> a4; // uses partial specialization #4, (X = int, T = char, I = 1)
A<int*, int*, 2> a5; // error: matches #2 (T = int, T2 = int*, I= 2)
                     //        matches #4 (X = int*, T = int, I = 2)
                     // neither one is more specialized than the other

비공식적으로 "A가 B보다 더 특수화되었다"는 것은 "A가 B가 받아들이는 타입의 부분집합을 받아들인다"는 의미입니다.

공식적으로, 부분 특수화 간의 더-전문화된-관계를 설정하기 위해, 각각은 다음과 같이 가상의 함수 템플릿으로 먼저 변환됩니다:

  • 첫 번째 함수 템플릿은 첫 번째 부분 특수화와 동일한 템플릿 매개변수를 가지며, 첫 번째 부분 특수화의 모든 템플릿 인자를 사용하는 클래스 템플릿 특수화 타입의 함수 매개변수를 단 하나만 가집니다.
  • 두 번째 함수 템플릿은 두 번째 부분 특수화와 동일한 템플릿 매개변수를 가지며, 두 번째 부분 특수화의 모든 템플릿 인자를 사용하는 클래스 템플릿 특수화 타입의 함수 매개변수를 단 하나만 가집니다.

함수 템플릿은 마치 function template overloading 을 위한 것처럼 순위가 매겨집니다.

template<int I, int J, class T> struct X {}; // 기본 템플릿
template<int I, int J>          struct X<I, J, int>
{
    static const int s = 1;
}; // 부분 특수화 #1
// #1에 대한 가상 함수 템플릿:
// template<int I, int J> void f(X<I, J, int>); #A
template<int I>                 struct X<I, I, int>
{
    static const int s = 2;
}; // 부분 특수화 #2
// #2에 대한 가상 함수 템플릿:
// template<int I>        void f(X<I, I, int>); #B
int main()
{
    X<2, 2, int> x; // #1과 #2 모두 매칭됨
// 함수 템플릿에 대한 부분 순서화:
// #A에서 #B: void(X<I, J, int>)에서 void(X<U1, U1, int>): 추론 성공
// #B에서 #A: void(X<I, I, int>)에서 void(X<U1, U2, int>): 추론 실패
// #B가 더 특수화됨
// #2가 인스턴스화되는 특수화임
    std::cout << x.s << '\n'; // 2 출력
}

부분 특수화의 멤버

부분 특수화의 멤버 템플릿 매개변수 목록과 템플릿 인수 목록은 부분 특수화의 매개변수 목록과 인수 목록과 일치해야 합니다.

기본 템플릿의 멤버들과 마찬가지로, 이들은 프로그램에서 사용되는 경우에만 정의하면 됩니다.

부분 특수화의 멤버들은 기본 템플릿의 멤버들과 관련이 없습니다.

부분 특수화의 멤버에 대한 명시적(완전) 특수화는 기본 템플릿의 명시적 특수화와 동일한 방식으로 선언됩니다.

template<class T, int I> // 기본 템플릿
struct A
{
    void f(); // 멤버 선언
};
template<class T, int I>
void A<T, I>::f() {}     // 기본 템플릿 멤버 정의
// 부분 특수화
template<class T>
struct A<T, 2>
{
    void f();
    void g();
    void h();
};
// 부분 특수화의 멤버
template<class T>
void A<T, 2>::g() {}
// 명시적(완전) 특수화
// 부분 특수화 멤버의
template<>
void A<char, 2>::h() {}
int main()
{
    A<char, 0> a0;
    A<char, 2> a2;
    a0.f(); // OK, 기본 템플릿의 멤버 정의 사용
    a2.g(); // OK, 부분 특수화의 멤버 정의 사용
    a2.h(); // OK, 부분 특수화 멤버의 완전 특수화 정의 사용
    a2.f(); // 오류: 부분 특수화 A<T,2>에서 f()의 정의가 없음
            // (기본 템플릿이 사용되지 않음)
}

기본 템플릿이 다른 클래스 템플릿의 멤버인 경우, 그 부분 특수화들은 둘러싸는 클래스 템플릿의 멤버입니다. 둘러싸는 템플릿이 인스턴스화되면, 각 멤버 부분 특수화의 선언도 마찬가지로 인스턴스화됩니다 (템플릿의 다른 모든 멤버들의 선언이 인스턴스화되는 방식과 동일하지만, 정의는 제외됩니다).

기본 멤버 템플릿이 둘러싸는 클래스 템플릿의 주어진 (암시적) 특수화에 대해 명시적으로(완전히) 특수화된 경우, 둘러싸는 클래스 템플릿의 이 특수화에 대해서는 멤버 템플릿의 부분 특수화들은 무시됩니다.

만약 멤버 템플릿의 부분 특수화가 둘러싼 클래스 템플릿의 주어진 (암시적) 특수화에 대해 명시적으로 특수화된 경우, 기본 멤버 템플릿과 그 외의 다른 부분 특수화들은 여전히 이 둘러싼 클래스 템플릿의 특수화에 대해 고려됩니다.

template<class T> struct A // 외부 클래스 템플릿
{
    template<class T2>
    struct B {};      // 기본 멤버 템플릿
    template<class T2>
    struct B<T2*> {}; // 멤버 템플릿의 부분 특수화
};
template<>
template<class T2>
struct A<short>::B {}; // 기본 멤버 템플릿의 완전 특수화
                       // (부분 특수화는 무시됨)
A<char>::B<int*> abcip;  // 부분 특수화 T2=int 사용
A<short>::B<int*> absip; // 기본 템플릿의 완전 특수화 사용 (부분 특수화 무시)
A<char>::B<int> abci;    // 기본 템플릿 사용

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 727 C++98 부분 및 완전 특수화가 클래스 범위에서
허용되지 않음
모든 범위에서 허용됨
CWG 1315 C++98 템플릿 매개변수가 id-표현식 이외의 상수
템플릿 인수에서 사용 불가
추론 가능한 경우 표현식 허용
CWG 1495 C++11 매개변수 팩 관련 명세가 불명확했음 특수화가 더 특수화되어야 함
CWG 1711 C++14 변수 템플릿 부분 특수화에 대한 명세 누락 변수 템플릿 지원 추가
CWG 1819 C++98 부분 특수화 정의를 위한 허용 범위 부분 특수화가 기본 템플릿과
동일한 범위에서 선언 가능하도록 함
CWG 2330 C++14 변수 템플릿에 대한 참조 누락 변수 템플릿 지원 추가

참고 항목