成员模板

来自cppreference.com
< cpp‎ | language

模板声明(函数变量 (C++14 起))可出现在任何不是局部类的 class 、 struct 或 union 的成员指定内。

#include <iostream>
#include <vector>
#include <algorithm>
 
struct Printer { // 泛型函子
    std::ostream& os;
    Printer(std::ostream& os) : os(os) {}
    template<typename T>
    void operator()(const T& obj) { os << obj << ' '; } // 成员模板
};
 
int main()
{
    std::vector<int> v = {1,2,3};
    std::for_each(v.begin(), v.end(), Printer(std::cout));
    std::string s = "abc";
    std::for_each(s.begin(), s.end(), Printer(std::cout));
}

输出:

1 2 3 a b c

成员模板的部分特化和出现在类作用域和外围命名空间作用域,但显式特化只能出现于外围命名空间作用域。

struct A {
    template<class T> struct B;         // 初等成员模板
    template<class T> struct B<T*> { }; // OK :部分特化
//  template<> struct B<int*> { };      // 错误:全特化
};
template<> struct A::B<int*> { };       // OK
template<class T> struct A::B<T&> { };  // OK

若外围类定义也是类模板,则在类体外定义成员模板时,它接收二个模板形参集:一个是外围类的,另一个是类自身的:

template<typename T1>
struct string {
    // 成员模板函数
    template<typename T2>
    int compare(const T2&);
    // 构造函数亦可为模板
    template<typename T2>
    string(const std::basic_string<T2>& s) { /*...*/ }
};
// string<T1>::compare<T2> 的类外定义 
template<typename T1> // 对于外围类模板
template<typename T2> // 对于成员模板
int string<T1>::compare(const T2& s) { /* ... */ }

目录

[编辑] 成员函数模板

析构函数和复制构造函数不能是模板。若声明了可用复制构造函数的类型签名实例化的模板构造函数,则替而使用隐式声明的复制构造函数。

成员函数模板不能为虚,且派生类中的成员函数模板不能覆写来自基类的虚成员函数。

class Base {
    virtual void f(int);
};
struct Derived : Base {
    // 此成员模板不覆写 Base::f
    template <class T> void f(T);
 
    // 非模板成员覆写可调用该模板:
    void f(int i) override {
         f<>(i);
    }
};

可以声明拥有相同名称的非模板成员函数和模板成员函数。在冲突的情况下(某些模板特化准确匹配非模板函数的签名),该名称的使用及类型指代非模板成员,除非提供显式模板实参列表。

template<typename T>
struct A {
    void f(int); // 非模板成员
 
    template<typename T2>
    void f(T2); // 成员模板
};
 
// 模板成员定义
template<typename T>
template<typename T2>
void A<T>::f(T2)
{
    // 一些代码
}
 
int main()
{
    A<char> ac;
    ac.f('c'); // 调用模板函数 A<char>::f<char>(int)
    ac.f(1);   // 调用非模板函数 A<char>::f(int)
    ac.f<>(1); // 调用模板函数 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 :等价声明
template<class V>
V X::good(V n) { return n; }
 
// 错误:不等价于 X 内的任何声明
template<class T>
T X::bad(typename identity<T>::type n) { return n; }

[编辑] 转换函数模板

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

struct A {
    template<typename T>
    operator T*(); // 转换到指向任何类型的指针
};
 
// 类外定义
template<typename T>
A::operator T*() {return nullptr;}
 
// 对 char* 的显式特化
template<>
A::operator char*() {return nullptr;}
 
// 显式实例化
template A::operator void*();
 
int main() {
    A a;
    int* ip = a.operator int*(); // 显式调用 A::operator int*()
}

重载决议中,名称查找不会找到转换函数模板的特化。取代地,所有可见转换函数模板都会受到考虑,且每个模板实参推导(对于转换函数模板有特殊规则)的特化都会得到使用,如同为名称查找所找到。

派生类中的 using 声明不能涉及来自基类的模板转换函数。

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

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

成员变量模板

变量模板可声明于类作用域内,该情况下它声明静态数据成员模板。细节见变量模板

(C++14 起)

[编辑] 缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

DR 应用于 出版时的行为 正确行为
CWG 1878 C++14 技术性允许 operator auto 禁止 operator auto