显式(完全)模板特化
允许为给定的模板实参集合自定义模板代码。
目录 |
[编辑] 语法
template <> 声明 |
|||||||||
以下任何项都可以完全特化
- 函数模板
- 类模板
- 变量模板(自 C++14 起)
- 成员函数 类模板的
- 静态数据成员 类模板的
- 成员类 类模板的
- 成员 枚举 类模板的
- 成员类模板 类或类模板的
- 成员函数模板 类或类模板的
- 成员变量模板 类或类模板的(自 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>&);
与特化具有相同名称和相同实参列表的函数不是特化(请参阅 函数模板 中的模板重载)。
默认函数实参 不能在函数模板、成员函数模板和类模板的成员函数的显式特化中指定,当类是隐式实例化时。
显式特化不能是 友元声明。
本节不完整 原因:审查不同 C++ 版本之间的异常规范要求 |
[编辑] 特化成员
当在类体外部定义显式特化类模板的成员时,不使用语法 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 | 尚不清楚 constinit 和 consteval 的 主模板是否延续到其显式特化中 |
不延续 |
CWG 2604 | C++11 | 尚不清楚主模板的属性 模板是否延续到其显式特化中 |
不延续 |