Namespaces
Variants

Explicit (full) 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
Template specialization
Parameter packs (C++11)
Miscellaneous

주어진 템플릿 인자 집합에 대한 템플릿 코드를 사용자 정의할 수 있습니다.

목차

구문

template <> 선언

다음 중 어느 것도 완전 특수화될 수 있습니다:

  1. function template
  2. class template
  3. variable template (since C++14)
  4. member function 클래스 템플릿의 멤버 함수
  5. static data member 클래스 템플릿의 정적 데이터 멤버
  6. member class 클래스 템플릿의 멤버 클래스
  7. 멤버 enumeration 클래스 템플릿의 멤버 열거형
  8. member class template 클래스 또는 클래스 템플릿의 멤버 클래스 템플릿
  9. member function template 클래스 또는 클래스 템플릿의 멤버 함수 템플릿
  10. 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 선언 이 될 수 없습니다.

특수화 멤버

명시적으로 특수화된 클래스 템플릿의 멤버를 클래스 본문 외부에서 정의할 때, 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 기본 템플릿의 속성들이 명시적 특수화에
상속되는지 불명확했음
상속되지 않음

참고 항목