跳转至

模板

1 主模板

如果要对模板进行特化,那么必须先要声明主模板

template<class T> class X; // 主模板(声明或者定义都可以)
template<> class X<int> { /*...*/ }; //模板特化

2 模板形参与实参

  • 类型 名字(可选) (1)
  • 类型 名字(可选) = 默认值 (2)
  • 类型 ... 名字(可选) (3)

注意:模板形参的名字是可以省略的

2.1 非类型模板形参

2.1.1 基本类型和枚举类型形参

enum EN {
    EN_1=0
};
template<typename T,int sz>
class A
{
public:
    T array[sz];
};
template<EN en>
class A1
{
public:
    void fun()
    {
        switch (en)
        {
        case EN_1:
            printf("EN_1");
            break;
        default:
            break;
        }
    }
};

2.1.2 函数指针形参

void print1(int i)
{
    printf("%d", i);
}
template<void(*f)(int)>
void fun1(int i)
{
    f(i);
}

template<typename T>
void print2(T i)
{
    printf("%d", i);
}

template<typename T,void(*f)(T)>
void fun2(T i)
{
    f(i);
}
int main()
{
    fun1<print1>(1);
    fun2<int, print1>(2);
    fun2<int, print2>(3.3);
}

2.2 参考资料

3 类模板

3.1 模板具体化

  • 隐式实例化
  • 解释:当使用具体的模板时,如有语句vector<int> vi;,则编译器会隐式生成vector<int>的定义,并实例化vi对象
  • 显式实例化
  • 关键字:template
  • 解释:通过template关键字直接告诉编译器要生成具体的类,如template class vector<int>;,此时编译器会生成vector<int>的具体类的声明。
  • 显式具体化
  • 关键字:template<>
  • 解释:为某一类型特化的类,形如template<> class_name<type_name>
  • 部分具体化
  • 解释:如多个模板参数的情况下,只具体化其中部分,形如template<class T> class_name<type_name,T>

显式实例化和隐式实例化不同之处在于,隐式实例化需要声明一个对象,如上面提及的vi

// 一般模板
template<class T1,class T2>
class A {
public:
    void print() { cout << "A<T1,T2>" << endl; }
};
// 显式具体化
template<>
class A<int,int> {
public:
    void print() { cout << "A<int,int>" << endl; }
};
// 部分具体化
template<class T>
class A<T,int> {
public:
    void print() { cout << "A<T,int>" << endl; }
};
// 显式实例化
template class A<string, float>;
int main()
{
    //隐式实例化
    A<float,float> af;
    af.print();//A<T1,T2> 调用一般模板
    A<int,int> ai;
    ai.print();//A<int,int> 调用具体化模板A<int,int>
    A<float, int> afi;
    afi.print();//A<T,int> 调用部分具体化模板A<T,int>
}

3.2 类模板类型

  • 非类型参数模板
  • 默认模板参数
  • 解释:可以为任何种类的模板形参(类型、非类型或模板)指定默认实参,如template<typename T1, typename T2 = int> class A;
  • 模板作为参数
template<typename T>
class A1 {
public:
    using _TYPE = T;
    _TYPE m_a;
    //A1():m_a(){}
    A1() = default;
    A1(const T& a):m_a(a){}
    A1& operator =(A1& a)
    {
        m_a=a.m_a;
        return *this;
    }
};
//模板作为参数
template<template<typename T>class A>
class B {
public:
};

//非类型参数模板,默认模板参数
template<typename T,int n=2>
class D
{
public:
    D() :value(){}
    T value[n];
};
int main()
{
    D<string,3> arr;
    B<A1> b1;

}

3.3 成员函数模板

  • 模板类中函数模板
  • 普通类中函数模板
//类模板中函数模板
template<typename T>
class E
{
public:
    using _TYPE = T;
    _TYPE _value;
    //类内实现
    template<typename U>
    void func1(U u){}
    //类内声明
    template<typename U>
    void func2(U u);
    template<typename X>
    class cl {
    public:
        X x;
    };
    cl<T> c1_T;
    cl<int> cl_i;
};
//类外实现 (双层template)
template<typename T>
template<typename U>
void E<T>::func2(U u){}
//普通类中函数模板
class F 
{
public:
    //类内实现
    template<typename T>
    void fun1(T t){}
    //类内声明
    template<typename T>
    void fun2(T t);
};
//类外实现
template<typename T>
void F::fun2(T t){}

3.4 模板类中自身类指针

template<typename T>
class G
{ 
public:
    G() :pg2(nullptr),pg1(nullptr) {}
    G* pg1;//默认与G<T>类型相同
    G<T>* pg2;//等价上式
};

3.5 友元模板类和友元模板函数

  • 知识点:
  • 友元模板类不需要前置声明
template<typename T>
class H
{
    T val;
public:
    void setValue(T v) { val = v; }
    H():val(T()){}
    //友元模板类
    template<typename U>
    friend class L;
    //友元函数
    template<typename ...U>
    friend auto sum(U... args)->decltype((args.val + ...));
};
template<typename T>
class L
{

};

template<typename...T>
auto sum(T...args)->decltype((args.val + ...))
{
  //c++17折叠表达式语法
    return (args.val + ...);
}
int main()
{
    H<int> h1;
    h1.setValue(2);
    H<int> h2;
    h2.setValue(3);
    sum(h1,h2);//输出5
}

4 函数模板

4.1 模板具体化

  • 隐式实例化
  • 解释:当使用具体的模板时,如有语句int a=2,b=1; swap(a,b);,则编译器会隐式生成void swap(int&,int&)的定义
  • 显式实例化
  • 关键字:template
  • 解释:通过template关键字直接告诉编译器要生成具体的函数,如template void swap(int&,int&),此时编译器会生成void swap(int&,int&)的函数定义。
  • 显式具体化
  • 关键字:template<>
  • 解释:为某一类型特化的函数,形如template<> ret_type fun_name(type_name)
  • 部分具体化
  • 解释:如多个模板参数的情况下,只具体化其中部分,形如template<class T> ret_type fun_name(type_name,T)
template<class T1,class T2>
void print(T1& t1, T2& t2) {
    cout <<"一般模板" <<endl;
}
template< class T>
void print(int& t1, T& t2) {
    cout << "部分具体化"<< endl;
}
template<>
void print(int& t1, int& t2) {
    cout << "显式具体化" << endl;
}
//显式实例化
template void print<double,double>(double&, double&);//在不调用此函数时,此函数已被声明
int main()
{
    float f = 1.1;
    double d = 2.1;
    int i = 1;
    print(i, f);//部分具体化
    print(i, i);//显式具体化
    print(f, f);//一般模板
    print(d, d);//一般模板
}

5 别名模板

  • 功能:简化复杂模板,特殊化模板参数
#include<array>
template<class T>
using arr10 = array<T, 10>;
int main()
{
    arr10<int> arr{1,2,3};
}

6 变量模板

  • 功能:变量模板定义一族变量或静态数据成员
// 全局作用域
template <class>
_INLINE_VAR constexpr bool is_lvalue_reference_v = false; // determine whether type argument is an lvalue reference

template <class _Ty>
_INLINE_VAR constexpr bool is_lvalue_reference_v<_Ty&> = true;

template <class>
_INLINE_VAR constexpr bool is_rvalue_reference_v = false; // determine whether type argument is an rvalue reference

template <class _Ty>
_INLINE_VAR constexpr bool is_rvalue_reference_v<_Ty&&> = true;
template<class T>
constexpr T PI = T(3.1415926);

//类作用域
struct limits {
    template<typename T>
    static const T min; // 静态数据成员模板的声明
};
template<typename T>
const T limits::min = { }; // 静态数据成员模板的定义

template<class T>
class X {
public:
    static T s; // 类模板的非模板静态数据成员的声明
    const static T s1=0; // 常量可以直接初始化
};
template<class T>
T X<T>::s = 0; // 类模板的非模板静态数据成员的定义
int main()
{
    bool b1 = is_lvalue_reference_v<int>;//false
    bool b2 = is_lvalue_reference_v<int&>;//true
    bool b3 = is_rvalue_reference_v<int>;//false
    bool b4 = is_rvalue_reference_v<int&&>;//true
    int i=PI<int>;
    float f = PI<float>;
    int i1=limits::min<int>;
    int i2 = X<int>::s;
}

7 待决名的typename和template消除歧义符

在一些源码中可能有这样的语法using _Ty2 = typename _Select<is_function_v<_Ty1>>::template _Apply<add_pointer<_Ty1>, remove_cv<_Ty1>>;,其中的typenametemplate到底起着什么作用呢? 解释如下: - typename在此用于说明这这_Select<is_function_v<_Ty1>>是一个类型 - template在此说明_Apply是一个模板

typename示例

struct A { // A 拥有嵌套变量 X 和嵌套类型 struct X
   struct X {};
   int X;
};
struct B {
    struct X { }; // B 拥有嵌套类型 struct X
};
template<class T> void f(T t) {
    typename T::X x;
}
void foo() {
    A a;
    B b;
    f(b); // OK:实例化 f<B>,T::X 指代 B::X
    f(a); // 错误:不能实例化 f<A>:
          // 因为 A::X 的有限定名字查找找到了数据成员
}

template示例

template<typename T>
struct S {
    template<typename U> void foo(){}
};

template<typename T>
void bar()
{
    S<T> s;
    s.foo<T>(); // 错误:< 被剖析为小于运算符
    s.template foo<T>(); // OK
}

关键词 template 仅可以这种方式用于运算符 ::(作用域解析)、->(通过指针的成员访问)和 .(成员访问)之后,下列表达式是所有合法示例:

T::template foo<X>();
s.template foo<X>();
this->template foo<X>();
typename T::template iterator<int>::value_type v;

更详细资料可参考cppreference