Partial template specialization
주어진 범주의 템플릿 인수에 대해 클래스 및 변수 (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 에 적용됩니다:
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부터) |
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
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) 템플릿이 인스턴스화될 때, 부분 특수화가 사용 가능한 경우 컴파일러는 기본 템플릿을 사용할지 아니면 그 부분 특수화 중 하나를 사용할지 결정해야 합니다.
// 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 | 변수 템플릿에 대한 참조 누락 | 변수 템플릿 지원 추가 |