命名空间
变体
操作

成员模板

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

模板声明(函数,以及 变量(since C++14))可以出现在任何非局部类的类、结构体或联合体的成员规范内部。

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
 
struct Printer
{
    // generic functor
    std::ostream& os;
    Printer(std::ostream& os) : os(os) {}
    template<typename T>
    void operator()(const T& obj) { os << obj << ' '; } // member template
};
 
int main()
{
    std::vector<int> v{1,2,3};
    std::for_each(v.begin(), v.end(), Printer(std::cout));
    std::string s{"abc"};
    std::ranges::for_each(s, Printer(std::cout));
}

输出

1 2 3 a b c

成员模板的偏特化可以出现在类作用域和封闭命名空间作用域中。显式特化可以出现在主模板可能出现的任何作用域中。

struct A
{
    template<class T> struct B;        // primary member template
    template<class T> struct B<T*> {}; // OK: partial specialization
//  template<> struct B<int*> {};      // OK via CWG 727: full specialization
};
template<> struct A::B<int*> {};       // OK
template<class T> struct A::B<T&> {};  // OK

如果封闭类声明本身是一个类模板,则当在类体外部定义成员模板时,它需要两组模板形参:一组用于封闭类,另一组用于自身

template<typename T1>
struct string
{
    // member template function
    template<typename T2>
    int compare(const T2&);
    // constructors can be templates too
    template<typename T2>
    string(const std::basic_string<T2>& s) { /*...*/ }
};
// out of class definition of string<T1>::compare<T2> 
template<typename T1> // for the enclosing class template
template<typename T2> // for the member template
int string<T1>::compare(const T2& s) { /* ... */ }

目录

[编辑] 成员函数模板

析构函数复制构造函数 不能是模板。如果声明的模板构造函数可以实例化为复制构造函数的类型签名,则会使用隐式声明的复制构造函数

成员函数模板不能是虚函数,并且派生类中的成员函数模板不能覆盖基类中的虚成员函数。

class Base
{
    virtual void f(int);
};
 
struct Derived : Base
{
    // this member template does not override Base::f
    template<class T> void f(T);
 
    // non-template member override can call the template:
    void f(int i) override
    {
         f<>(i);
    }
};

可以声明具有相同名称的非模板成员函数和模板成员函数。在发生冲突时(当某些模板特化与非模板函数签名完全匹配时),对该名称和类型的使用指的是非模板成员,除非提供了显式模板实参列表。

template<typename T>
struct A
{
    void f(int); // non-template member
 
    template<typename T2>
    void f(T2); // member template
};
 
// template member definition
template<typename T>
template<typename T2>
void A<T>::f(T2)
{
    // some code
}
 
int main()
{
    A<char> ac;
    ac.f('c'); // calls template function A<char>::f<char>(char)
    ac.f(1);   // calls non-template function A<char>::f(int)
    ac.f<>(1); // calls template function A<char>::f<int>(int)
}


成员函数模板的类外定义必须与类内部的声明等效(有关等效性的定义,请参见函数模板重载),否则它将被视为重载。

struct X
{
    template<class T> T good(T n);
    template<class T> T bad(T n);
};
 
template<class T> struct identity { using type = T; };
 
// OK: equivalent declaration
template<class V>
V X::good(V n) { return n; }
 
// Error: not equivalent to any of the declarations inside X
template<class T>
T X::bad(typename identity<T>::type n) { return n; }

[编辑] 转换函数模板

用户定义的 转换函数 可以是模板。

struct A
{
    template<typename T>
    operator T*(); // conversion to pointer to any type
};
 
// out-of-class definition
template<typename T>
A::operator T*() { return nullptr; }
 
// explicit specialization for char*
template<>
A::operator char*() { return nullptr; }
 
// explicit instantiation
template A::operator void*();
 
int main()
{
    A a;
    int* ip = a.operator int*(); // explicit call to A::operator int*()
}

重载决议期间,名称查找找不到转换函数模板的特化。而是考虑所有可见的转换函数模板,并且通过模板实参推导(对于转换函数模板具有特殊规则)产生的所有特化都像通过名称查找找到一样使用。

派生类中的 using 声明不能引用基类中模板转换函数的特化。

用户定义的转换函数模板不能具有推导的返回类型

struct S
{
    operator auto() const { return 10; } // OK
    template<class T> operator auto() const { return 42; } // error
};
(自 C++14 起)

成员变量模板

变量模板声明可以出现在类作用域中,在这种情况下,它声明一个静态数据成员模板。有关详细信息,请参见变量模板

(自 C++14 起)

[编辑] 缺陷报告

以下行为更改的缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布行为 正确行为
CWG 1878 C++14 技术上允许 operator auto 禁止 operator auto