命名空间
变体
操作

显式(完全)模板特化

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围 for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 在 C++11 中弃用*
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储持续期说明符
初始化
 
 
 
 

允许为给定的模板实参集合自定义模板代码。

目录

[编辑] 语法

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> // primary template
    class X { /*...*/ };
    template<>        // specialization in same namespace
    class X<int> { /*...*/ };
 
    template<class T> // primary template
    class Y { /*...*/ };
    template<>        // forward declare specialization for double
    class Y<double>;
}
 
template<> // OK: specialization in same namespace
class N::Y<double> { /*...*/ };

特化必须在每次使用会导致隐式实例化之前声明,在发生此类使用的每个翻译单元中

class String {};
 
template<class T>
class Array { /*...*/ };
 
template<class T> // primary template
void sort(Array<T>& v) { /*...*/ }
 
void f(Array<String>& v)
{
    sort(v); // implicitly instantiates sort(Array<String>&), 
}            // using the primary template for sort()
 
template<> // ERROR: explicit specialization of sort(Array<String>)
void sort<String>(Array<String>& v); // after implicit instantiation

已声明但未定义的模板特化可以像任何其他 不完整类型 一样使用(例如,可以使用指向它的指针和引用)

template<class T> // primary template
class X;
template<>        // specialization (declared, not defined)
class X<int>;
 
X<int>* p; // OK: pointer to incomplete type
X<int> x;  // error: object of incomplete type

函数或变量(自 C++14 起)模板的显式特化是否为 inline/constexpr(自 C++11 起)/constinit/consteval(自 C++20 起) 由显式特化本身决定,而与主模板是否使用该说明符声明无关。 同样,出现在模板声明中的 属性 对该模板的显式特化没有影响:(自 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]] has no effect, but [[maybe_unused]] has
}

[编辑] 函数模板的显式特化

当特化函数模板时,如果 模板实参推导 可以从函数实参中提供它们,则可以省略其模板实参

template<class T>
class Array { /*...*/ };
 
template<class T> // primary template
void sort(Array<T>& v);
template<>        // specialization for T = int
void sort(Array<int>&);
 
// no need to write
// template<> void sort<int>(Array<int>&);

与特化具有相同名称和相同实参列表的函数不是特化(请参阅 函数模板 中的模板重载)。

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

显式特化不能是 友元声明

[编辑] 特化成员

当在类体外部定义显式特化类模板的成员时,不使用语法 template<>,除非它是显式特化的成员类模板的成员,该成员类模板被特化为类模板,因为否则,语法将要求此类定义以 template<参数> 开头,这是嵌套模板要求的

template<typename T>
struct A
{
    struct B {};      // member class 
 
    template<class U> // member class template
    struct C {};
};
 
template<> // specialization
struct A<int> 
{
    void f(int); // member function of a specialization
};
// template<> not used for a member of a specialization
void A<int>::f(int) { /* ... */ }
 
template<> // specialization of a member class
struct A<char>::B
{
    void f();
};
// template<> not used for a member of a specialized member class either
void A<char>::B::f() { /* ... */ }
 
template<> // specialization of a member class template
template<class U>
struct A<char>::C
{
    void f();
};
 
// template<> is used when defining a member of an explicitly
// specialized member class template specialized as a class template
template<>
template<class U>
void A<char>::C<U>::f() { /* ... */ }


模板的静态数据成员的显式特化是一个定义,如果声明包含初始化器;否则,它是一个声明。这些定义必须使用花括号进行默认初始化

template<>
X Q<int>::x;    // declaration of a static member
template<>
X Q<int>::x (); // error: function declaration
template<>
X Q<int>::x {}; // definition of a default-initialized static member

类模板的成员或成员模板可以针对类模板的给定隐式实例化显式特化,即使成员或成员模板是在类模板定义中定义的。

template<typename T>
struct A
{
    void f(T);         // member, declared in the primary template
 
    void h(T) {}       // member, defined in the primary template
 
    template<class X1> // member template
    void g1(T, X1);
 
    template<class X2> // member template
    void g2(T, X2);
};
 
// specialization of a member
template<>
void A<int>::f(int);
 
// member specialization OK even if defined in-class
template<>
void A<int>::h(int) {}
 
// out of class member template definition
template<class T>
template<class X1>
void A<T>::g1(T, X1) {}
 
// member template specialization
template<>
template<class X1>
void A<int>::g1(int, X1);
 
// member template specialization
template<>
template<>
void A<int>::g2<char>(int, char); // for X2 = char
 
// same, using template argument deduction (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<实参>,因为这些特化本身就是模板

template<class T1>
class A
{
    template<class T2>
    class B
    {
        template<class T3> // member template
        void mf1(T3);
 
        void mf2();        // non-template member
    };
};
 
// specialization
template<>        // for the specialized A
template<class X> // for the unspecialized B
class A<int>::B
{
    template<class T>
    void mf1(T);
};
 
// specialization
template<>        // for the specialized A
template<>        // for the specialized B
template<class T> // for the unspecialized mf1
void A<int>::B<double>::mf1(T t) {}
 
// ERROR: B<double> is specialized and is a member template, so its enclosing A
// must be specialized also
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 尚不清楚 constinitconsteval
主模板是否延续到其显式特化中
不延续
CWG 2604 C++11 尚不清楚主模板的属性
模板是否延续到其显式特化中
不延续

[编辑] 参见