概述

  • 函数模板 编译器推断类型,声明和定义都在头文件中,
    1.类型参数

    template <typename T> foo(T* p){
        T tmp=*p;
        return tmp;
    }

    2.非类型模板,常量表达式值

  • 类模板 需显示指出类型
  • 实例化:指的是生成模板代码。显示实例化关键词是template ;会实例化所有成员,控制在一处实例化,其他用extern template引入
  • 特化:特化是某些情况下使用特定的模板。关键词是template <> 全特化或者对于类可以部分特化或全特化 template <typename T1>
  • 模板别名:
    不能用typedef blob <string> strBlob; => <T>错误。
    template <typename T> using twin=pair<T,T>
  • 模板的默认参数
    template<class T1, class T2=int>
    class A{};

函数模板类型推断

  • 只有const转化,数组、函数到指针的转换,派生类到基类等都不能应用于模板参数
  • 若两个模板名称一样,实例化类型也要一致
  • 判断不出类型的需要显示指定

    T1 sum(T2,T3) 
    auto val=sum<long>(i,lng)  //long对应T1
  • 返回类型

    template <typename It>
    auto fun(It beg,It end)->decltype(*beg)
    //返回非指针,元素的值
    auto fun(It beg,It end)->typename remove_reference<decltype(*beg)>::type;
    //typename表示返回类型,非static变量,remove_reference若T为X&,X&&则为X,否则T。
    //类似的还有remote_pointer,add_const等
    
  • 引用

    int i=42;
    int &r = i; //左值,左值引用(能取地址的为左值)
    const int &r3 = i*42 //右值,const&不可改变,可绑定在一个右值上 ,int &r2=i*42则错误
    int &&rr2=i*42 //右值,右值引用
    
    template <typename T> void f1(T&),        f1(ci)✅   f1(i)✅   f1(5)❌
    template <typename T> void f2(const T&),  f2(ci)✅   f2(i)✅   f2(5)✅
    template <typename T> void f3(T&&),       f3(ci)✅   f3(i)✅   f3(5)✅
    //f3(i)因为引用折叠:X& &,X& &&,X&& & => X& ; X&& &&=>X&&

    std::move

    int &&rr3=std::move(rr1) //显示将一个左值转移为对应右值引用类型
    typename name_reference<T>::type&& move(T&& t){
        return static_cast<typename name_reference<T>::type&&>(t)
    }

    std::forward

    //从前面看出&&可以接受任何左值或者右值参数,但当参数转发时,第一层T用&&解出后不能用于下层,使用std::forward保持类型
    void flip(F f,T1&& t1,T2&& t2){
        f(std::forward<T2>(t2),std::forward<T1>(t1));
    }
  • auto与template匹配
    template <typename T> void f(ParamType param);
    1.paramType既不是指针也不是引用
    template <typename T> void f(T param);
    如果expr是一个引用,那么先忽略其引用类型;如果有const,volatile修饰符,则一并忽略。

    int x = 27; 
    const int cx = x;
    auto x1 = x;        // type is int
    auto x2 = x;        // type is int

    2.ParamType是一个指针或者引用,但不是全局引用
    忽略引用
    template <typename T> void f(T& param); //或T*

    int x = 27;           // x is an int
    const int cx = x;     // cx is a const int
    const int& rx = x;    // rx is a reference to x as a const int
    auto& x1 = x;         // int&
    auto& x2 = cx;        // const int&
    auto& x3 = rx;        // const int&

    3.ParamType是一个全局引用
    template <typename T> void f(T&& param);
    如果expr是一个左值,形式为A或者A&。那么T和ParamType的类型都会被推导为A&。
    如果expr是一个右值,形式为A&&。则T的类型会被推导为A,而ParamType的类型会被推导为A&&。
    auto && x1=x; //x为右值 ,x1为x&&,否则x& ,这个只在for中用,忽略吧
    4.数组类型参数
    而在形参为引用(包括左值引用和全局引用)的时候则会推导出数组的类型,不会退化为指针

    const char name[] = "J.P.Briggs";
    template <typename T> void f1(T& param); 
     f1(name);  // T's type and param's type are const char(&)[13]
  • decltype
    在C++ 11中,他唯一的作用就是声明一个返回类型依赖于模板参数的模板函数,这个功能在使用auto时会出错

    template <typename Container, typename Index>
    auto access(Container& c, Index i) -> decltype(auto)  
    {
      return c[i];
    }

    C++14

    template <typename Container, typename Index>
    decltype(auto) access(Container&& c, Index i)  // final C++14 version
    {
        return std::forward<Container>(c)[i];
    }

    decltype(e)的类型有如下定义:

    如果e是一个没有给括号包围的变量名或者一个没有被括号包围的类成员访问,则decltype(e)的类型就是最终访问的变量的类型。如果访问的变量并不存在,则视为错误。

    如果; e是一个函数调用或者重载操作符(如果有括号包围,则去除括号),则decltype(e)是最终所调用函数的返回值类型;

    否则,如果e是一个左值,且e的类型是T,则decltype(e)为T&;

    否则 ,decltype(e)的类型就是e的类型。

    const int&& foo();
    int i;
    struct A { double x; };
    const A* a = new A();
    decltype(foo()) x1 = std::move(i); // type is const int&&
    decltype(i) x2; // type is int
    decltype(a->x) x3; // type is double
    decltype((a->x)) x4 = x3; // type is const double&

函数重载

参数个数

template <class T>
void printarg(T t)
{
   cout << t << endl;
}

template <class ...Args>
void expand(Args... args)
{
   int arr[] = {(printarg(args), 0)...};
}

expand(1,2,3,4);

特化

  • 函数模板全特化
    template<>
    匹配:非模板>特化>其他
  • 函数重载

    template<typename T, class N> void compare(T num1, N num2)  //原模板
    template<> void compare(int num1, double num2)   //全特化
    template<int,double> void compare(int num1, double num2);    template<int,class N> void compare(int num1, N num2)  //报错
    template<class N> void compare(int num1, N num2)  //函数重载
  • 类模板全特化
  • 类模板部分特化

类型参数,非类型参数
正常遇到才生成代码,有时候需要重新连接。
显示实例化,无论是否遇到都生成代码,只是对编译连接的一种优化
专门化,对于特定的类型不按照模板,一种重载


梦想家
107 声望76 粉丝