Explicit (full) template specialization
주어진 템플릿 인자 집합에 대한 템플릿 코드를 사용자 정의할 수 있습니다.
목차 |
구문
template <>
선언
|
|||||||||
다음 중 어느 것도 완전 특수화될 수 있습니다:
- function template
- class template
- variable template (since C++14)
- member function 클래스 템플릿의 멤버 함수
- static data member 클래스 템플릿의 정적 데이터 멤버
- member class 클래스 템플릿의 멤버 클래스
- 멤버 enumeration 클래스 템플릿의 멤버 열거형
- member class template 클래스 또는 클래스 템플릿의 멤버 클래스 템플릿
- member function template 클래스 또는 클래스 템플릿의 멤버 함수 템플릿
- member variable template 클래스 또는 클래스 템플릿의 멤버 변수 템플릿 (since C++14)
예를 들어,
#include <type_traits> template<typename T> // primary template struct is_void : std::false_type {}; template<> // explicit specialization for T = void struct is_void<void> : std::true_type {}; int main() { static_assert(is_void<char>::value == false, "for any type T other than void, the class is derived from false_type"); static_assert(is_void<void>::value == true, "but when T is void, the class is derived from true_type"); }
상세히
명시적 특수화는 해당 주 템플릿이 정의될 수 있는 모든 범위에서 선언될 수 있습니다 (이는 주 템플릿이 정의된 범위와 다를 수 있습니다. 예를 들어 멤버 템플릿 의 클래스 외부 특수화와 같은 경우). 명시적 특수화는 비특수화된 템플릿 선언 이후에 나타나야 합니다.
namespace N { template<class T> // 기본 템플릿 class X { /*...*/ }; template<> // 동일 네임스페이스 내 특수화 class X<int> { /*...*/ }; template<class T> // 기본 템플릿 class Y { /*...*/ }; template<> // double에 대한 특수화 전방 선언 class Y<double>; } template<> // OK: 동일 네임스페이스 내 특수화 class N::Y<double> { /*...*/ };
특수화는 암시적 인스턴스화를 유발하는 첫 번째 사용 이전에, 그러한 사용이 발생하는 모든 번역 단위에서 선언되어야 합니다:
class String {}; template<class T> class Array { /*...*/ }; template<class T> // 기본 템플릿 void sort(Array<T>& v) { /*...*/ } void f(Array<String>& v) { sort(v); // sort(Array<String>&)의 암시적 인스턴스화 수행, } // sort()의 기본 템플릿 사용 template<> // 오류: sort(Array<String>)의 명시적 특수화 void sort<String>(Array<String>& v); // 암시적 인스턴스화 이후
선언되었지만 정의되지 않은 템플릿 특수화는 다른 불완전한 타입 과 마찬가지로 사용할 수 있습니다(예: 해당 타입에 대한 포인터와 참조를 사용할 수 있음):
template<class T> // 기본 템플릿 class X; template<> // 특수화 (선언만 되고 정의되지 않음) class X<int>; X<int>* p; // OK: 불완전 타입에 대한 포인터 X<int> x; // 오류: 불완전 타입의 객체
함수
또는 변수
(since C++14)
템플릿의 명시적 특수화가
inline
/
constexpr
(since C++11)
/
constinit
/
consteval
(since C++20)
인지는 기본 템플릿이 해당 지정자로 선언되었는지와 관계없이 명시적 특수화 자체에 의해 결정됩니다.
마찬가지로,
속성
이 템플릿의 선언에 나타나는 경우 해당 템플릿의 명시적 특수화에는 영향을 미치지 않습니다:
(since C++11)
template<class T> void f(T) { /* ... */ } template<> inline void f<>(int) { /* ... */ } // OK, inline template<class T> inline T g(T) { /* ... */ } template<> int g<>(int) { /* ... */ } // OK, not inline template<typename> [[noreturn]] void h([[maybe_unused]] int i); template<> void h<int>(int i) { // [[noreturn]]은 효과가 없지만, [[maybe_unused]]는 효과가 있음 }
함수 템플릿의 명시적 특수화
함수 템플릿을 특수화할 때, template argument deduction 이 함수 인자들로부터 제공할 수 있다면 템플릿 인자들을 생략할 수 있습니다:
template<class T> class Array { /*...*/ }; template<class T> // 기본 템플릿 void sort(Array<T>& v); template<> // T = int에 대한 특수화 void sort(Array<int>&); // 작성할 필요 없음 // template<> void sort<int>(Array<int>&);
동일한 이름과 동일한 인수 목록을 가진 함수는 특수화가 아닙니다 ( function template 에서 템플릿 오버로딩 참조).
기본 함수 인수 는 함수 템플릿, 멤버 함수 템플릿의 명시적 특수화 및 클래스가 암시적으로 인스턴스화될 때 클래스 템플릿의 멤버 함수에서 지정할 수 없습니다.
명시적 특수화는 friend 선언 이 될 수 없습니다.
|
이 섹션은 불완전합니다
이유: 다른 C++ 버전 간의 exception specification 요구사항 검토 필요 |
특수화 멤버
명시적으로 특수화된 클래스 템플릿의 멤버를 클래스 본문 외부에서 정의할 때, template <> 구문은 사용되지 않습니다. 단, 클래스 템플릿으로 특수화된 명시적으로 특수화된 멤버 클래스 템플릿의 멤버인 경우는 예외입니다. 그렇지 않으면 해당 정의가 중첩된 템플릿에 의해 요구되는 template < parameters > 로 시작해야 하기 때문입니다.
template<typename T> struct A { struct B {}; // 멤버 클래스 template<class U> // 멤버 클래스 템플릿 struct C {}; }; template<> // 특수화 struct A<int> { void f(int); // 특수화의 멤버 함수 }; // 특수화의 멤버에는 template<>를 사용하지 않음 void A<int>::f(int) { /* ... */ } template<> // 멤버 클래스의 특수화 struct A<char>::B { void f(); }; // 특수화된 멤버 클래스의 멤버에도 template<>를 사용하지 않음 void A<char>::B::f() { /* ... */ } template<> // 멤버 클래스 템플릿의 특수화 template<class U> struct A<char>::C { void f(); }; // 클래스 템플릿으로 특수화된 멤버 클래스 템플릿의 멤버를 정의할 때는 template<>를 사용함 template<> template<class U> void A<char>::C<U>::f() { /* ... */ }
템플릿의 정적 데이터 멤버에 대한 명시적 특수화는 선언에 초기화자가 포함된 경우 정의이며, 그렇지 않으면 선언입니다. 이러한 정의는 기본 초기화를 위해 중괄호를 사용해야 합니다:
template<> X Q<int>::x; // 정적 멤버 선언 template<> X Q<int>::x (); // 오류: 함수 선언 template<> X Q<int>::x {}; // 기본 초기화된 정적 멤버 정의
클래스 템플릿의 멤버 또는 멤버 템플릿은 해당 멤버나 멤버 템플릿이 클래스 템플릿 정의 내에서 정의된 경우에도, 클래스 템플릿의 특정 암시적 인스턴스화에 대해 명시적으로 특수화될 수 있습니다.
template<typename T> struct A { void f(T); // 멤버 함수, 기본 템플릿에서 선언됨 void h(T) {} // 멤버 함수, 기본 템플릿에서 정의됨 template<class X1> // 멤버 템플릿 void g1(T, X1); template<class X2> // 멤버 템플릿 void g2(T, X2); }; // 멤버 함수의 특수화 template<> void A<int>::f(int); // 클래스 내부에서 정의된 경우에도 멤버 특수화 가능 template<> void A<int>::h(int) {} // 클래스 외부 멤버 템플릿 정의 template<class T> template<class X1> void A<T>::g1(T, X1) {} // 멤버 템플릿 특수화 template<> template<class X1> void A<int>::g1(int, X1); // 멤버 템플릿 특수화 template<> template<> void A<int>::g2<char>(int, char); // X2 = char인 경우 // 동일한 작업, 템플릿 인자 추론 사용 (X1 = char) template<> template<> void A<int>::g1(int, char);
멤버 또는 멤버 템플릿은 여러 개의 외부 클래스 템플릿 내에 중첩될 수 있습니다. 이러한 멤버에 대한 명시적 특수화에서는 template <> 를 명시적으로 특수화하는 모든 외부 클래스 템플릿에 대해 하나씩 존재합니다.
template<class T1> struct A { template<class T2> struct B { template<class T3> void mf(); }; }; template<> struct A<int>; template<> template<> struct A<char>::B<double>; template<> template<> template<> void A<char>::B<char>::mf<double>();
이러한 중첩 선언에서 일부 레벨은 비특수화된 상태로 남을 수 있습니다(단, 외부 클래스가 비특수화된 상태에서는 네임스페이스 범위에서 클래스 멤버 템플릿을 특수화할 수 없음). 이러한 각 레벨에 대해 선언에는 template < arguments > 가 필요합니다. 왜냐하면 이러한 특수화 자체가 템플릿이기 때문입니다:
template<class T1> class A { template<class T2> class B { template<class T3> // 멤버 템플릿 void mf1(T3); void mf2(); // 비템플릿 멤버 }; }; // 특수화 template<> // 특수화된 A에 대해 template<class X> // 비특수화된 B에 대해 class A<int>::B { template<class T> void mf1(T); }; // 특수화 template<> // 특수화된 A에 대해 template<> // 특수화된 B에 대해 template<class T> // 비특수화된 mf1에 대해 void A<int>::B<double>::mf1(T t) {} // 오류: B<double>은 특수화되었고 멤버 템플릿이므로 이를 포함하는 A도 // 반드시 특수화되어야 함 template<class Y> template<> void A<Y>::B<double>::mf2() {}
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 531 | C++98 |
명시적 특수화의 멤버를 네임스페이스 범위에서
정의하는 구문이 명시되지 않았음 |
명시됨 |
| CWG 727 | C++98 |
부분 특수화와 완전 특수화가 클래스 범위에서
허용되지 않았음 |
모든 범위에서 허용됨 |
| CWG 730 | C++98 |
비템플릿 클래스의 멤버 템플릿을 완전히
특수화할 수 없었음 |
허용됨 |
| CWG 2478 | C++20 |
기본 템플릿의
constinit
와
consteval
이
명시적 특수화에 상속되는지 불명확했음 |
상속되지 않음 |
| CWG 2604 | C++11 |
기본 템플릿의 속성들이 명시적 특수화에
상속되는지 불명확했음 |
상속되지 않음 |