第十四章:模版(四)

数值模版参数与模版模版参数

模版可以接收(编译期常量)数值作为模版参数

  • template <int a>
    template <int a>
    
  • template <typename T,T val>
template <typename T,T val>
int fun(int x){
    return x + val;
}

  • (C++17)template <auto a>
template <auto a>
void fun(){

}
  • (C++20)接收字面值类对象与浮点数作为模版参数

    目前clang 12不支持接收浮点数作为模版参数(主要是由于浮点数的储存方式和舍入误差)

模版同时也可以接收模版作为模版参数

  • template<template<typename. T> class C>
  • (C++17)template<template<typename T> typename C>
  • C++17开始,模板的模板实参考虑缺省模板实参( clang 12 支持程度有限)

别名模版

我们可以使用using来引入别名模版

template <typename T>
using MyType = T;


int main() {
    MyType<int> x;
}
  • 为模版本身引入别名
    template <class T>
    struct Alloc{};
    template <class T>
    using Vec = std::vector<T,Alloc<T>>;
    Vec<int> v;
    
  • 为类模版的成员引入别名
    template <typename T>
    struct B{
        using TP = T*;
    };
    
    template <typename T>
    using MyPointer = typename B<T>::TP ;
    
    int main() {
        MyPointer<int> x;
    }
    
  • 别名模版不支持特化,但可以基于类模版的特化引入别名,以实现类似特化的功能
    template <typename T>
    struct B{
        using type = T*;
    };
    
    template<>
    struct B<int>{
        using type = int &;
    };
    
    template <typename T>
    using MyPointer = typename B<T>::type ;
    

变长模版

我们可以通过使用参数包来引入变长模版

template <typename... a>
void fun(){
    
}

int main() {
    fun<int,double,char>();
}

关于变长模版的具体内容参考这里

在引入变长模版之后,我们需要关注一个非常重要的概念-包展开。我们可以通过包展开技术操作变长模版参数,具体的内容参考这里

void fun(){\
}

template <typename U, typename ... T>
void fun(U u, T... args){
    std::cout << u << std::endl;
    fun(args...);
}
        
        
int main() {
    fun(1,2,"hello",'c');
    // 结果为 1
             2
             hello
             c
}

在C++17中引入了折叠表达式,可以对上述的代码进行简化

template <typename... T>
void fun(T... args){
    ((std::cout << args << std::endl),...);
}


int main() {
    fun(1,2,"hello",'c');
    // 结果为 1
             2
             hello
             c
}

这里只是最简单的例子,具体的用法可以参考这里

注意,折叠表达式用于表达式求值,无法处理输入(输出)是类型与模板的情形

完美转发

在模版中可以使用foeward函数,它通常与万能引用结合使用,可以同时处理传入参数是左值或右值的情形

void g(int &){
    std::cout << "l-referernce\n";
}

void g(int &&){
    std::cout << "r-referernce\n";
}

template <typename T>
void fun(T&& input){
    std::cout << "Hello word\n";
    g(std::forward<T>(input));			// 如果不加forward,由于右值引用的变量本身是左值,会导致不期望的结果
}

int main() {
    int x = 3;
    fun(x);
    fun(3);
}

关于完美转发的具体使用方法,请参考这里

Lambda表达式模版

在C++20中引入了Lambda表达式模版,具体可以参考这里

消除歧义

我们可以使用typenametemplate消除歧义

  • 使用typename表示一个依赖名称是类型而非静态数据成员
template <typename T>
void fun(){
    T::internal * p;        //两种理解 1,指针 2,乘法
}
template <typename T>
void fun(){
    typename  T::internal * p;        // 消除了歧义,只认为是指针
}
  • 使用template表示一个依赖名称是模版
struct Str{
    template<typename T>
    static void internal(){

    }
};

template <typename T>
void fun(){
    T::internal<int>();         // 两种理解 1,T内部的模版函数 2,<理解为小于号 不幸的是编译器会理解为第二个
}
struct Str{
    template<typename T>
    static void internal(){

    }
};

template <typename T>
void fun(){
    T::template internal <int>();         // 消除了歧义,只认为是T内部的函数模版
}

变量模版(C++14)

从C++14开始引入了变量模版

template <typename T>
T pi = (T)3.1415926;

int main(){
pi<int>;			// 3
pi<float>;			// 3.1415926
}
template <typename T, unsigned v>
unsigned MySize = (sizeof(T) == v);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值