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. 函数模板
  2. 类模板
  3. 变量模板 (C++14 起)
  4. 类模板的 成员函数
  5. 类模板的 静态数据成员
  6. 类模板的 成员类
  7. 类模板的成员 枚举
  8. 类或类模板的 成员类模板
  9. 类或类模板的 成员函数模板
  10. 类或类模板的 成员变量模板 (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<> // 正确:同一命名空间内的特化
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; // 正确:指向不完整类型的指针
X<int> x;  // 错误:不完整类型的对象

函数模板 或变量模板 (C++14 起) 的显式特化是否为 inline / constexpr (C++11 起) / constinit / consteval (C++20 起) 由显式特化自身决定,与主模板是否声明该说明符无关。 类似地,模板声明中出现的 属性 不会影响该模板的显式特化: (C++11 起)

template<class T>
void f(T) { /* ... */ }
template<>
inline void f<>(int) { /* ... */ } // 正确,使用 inline
template<class T>
inline T g(T) { /* ... */ }
template<>
int g<>(int) { /* ... */ }         // 正确,未使用 inline
template<typename>
[[noreturn]] void h([[maybe_unused]] int i);
template<> void h<int>(int i)
{
    // [[noreturn]] 无作用,但 [[maybe_unused]] 仍有效
}

函数模板的显式特化

当特化函数模板时,若 模板实参推导 能够从函数实参中获取模板参数,则可省略其模板实参:

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 中的 函数模板重载 )。

默认函数参数 不能在函数模板的显式特化、成员函数模板以及类模板的成员函数中指定,当类被隐式实例化时。

显式特化不能作为 友元声明

特化的成员

当在显式特化的类模板外部定义其成员时,除以下情况外不使用 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>();
说明:由于所有内容均位于 `
` 标签内的 C++ 代码块中,根据要求保留了所有代码和 HTML 标签的原始格式,未进行任何翻译。

在此类嵌套声明中,某些层级可能保持未特化状态(除非当外层类未特化时,不能在命名空间作用域特化其类成员模板)。对于每个这样的层级,声明需要 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++标准。

缺陷报告 适用标准 发布时行为 正确行为
CWG 531 C++98 未指定在命名空间作用域定义显式特化成员的语法 已指定
CWG 727 C++98 不允许在类作用域中进行部分和完全特化 允许在任何作用域中进行
CWG 730 C++98 非模板类的成员模板无法完全特化 允许
CWG 2478 C++20 主模板的 constinit consteval 是否
会继承到其显式特化中不明确
不继承
CWG 2604 C++11 主模板的属性是否会继承到其显式特化中不明确 不继承

另请参阅