【C++】16.模板[深蓝学院C++第14章]

前言

 

一.函数模板

1.1 template关键字

template<typename T> void fun(T input){...}

函数模板不是函数

函数模板的声明与定义,声明可以多次、定义只能一次,

typename关键字可以替换为class,含义相同

函数模板中包含了两对参数:T是模板形参,input是函数形参

1.2 函数模板的显式实例化

fun<int>(3);//int是模板实参,3是函数实参

实例化会使得编译器产生相应的函数(函数模板并非函数,不能调用)

编译器的两阶段处理:

(1)模板语法检查

(2)模板实例化

模板必须在实例化时可见,翻译单元的一处定义原则

注意模板与内联函数的异同,inline是要在调用处展开,模板是要在翻译单元处可见完成实例化

1.3 函数模板的重载

 1 template <typename T>
 2 void fun(T input){
 3     std::cout << input << std::endl;
 4 }
 5 
 6 template <typename T,typename T2>
 7 void fun(T input,T2 input2){
 8     std::cout << input << std::endl;
 9     std::cout << input2 << std::endl;
10 }

 

1.4 模板实参的类型推导

如果函数模板在实例化时没有显式指定模板实参,那么系统会尝试进行推导

推导是根据表达式进行推断,规则与auto的类型推导相似

(1)函数形参是左值引用 / 指针:
    -忽略表达式类型中的引用
    -将表达式类型与函数形参模式匹配以确定模板实参
(2)函数形参是万能引用
    – 如果实参表达式是右值,那么模板形参被推导为去掉引用的基本类型
    – 如果实参表达式是左值,那么模板形参被推导为左值引用,触发引用折叠
(3)函数形参不包含引用
    – 忽略表达式类型中的引用
    – 忽略顶层 const
    – 数组、函数转换成相应的指针类型

1.5 模板实参不能被推导得到

如果模板形参与函数形参无关,则无法推导

推导成功也可能因为存在歧义而无法使用

1.6 无法推导时的缺省模板实参

template <typename T =  int>

void fun(乱七八糟的T)

可以为任意位置的模板形参指定缺省模板实参,注意与函数缺省实参的区别

1.7 显式指定部分模板实参

显式指定的模板实参必须从最左边开始,依次指定

模板形参的声明顺序会影响调用的灵活性

1.8 自动推导时的可能情况

函数形参无法匹配 —— SFINAE (替换失败并非错误)

模板与非模板同时匹配,匹配等级相同,此时选择非模板的版本

多个模板同时匹配,此时采用偏序关系确定选择 “ 最特殊 ” 的版本

1.9 函数模板的实例化控制

显式实例化定义: template void fun<int>(int) / template void fun(int)

显式实例化声明: extern template void fun<int>(int) / extern template void fun(int)

注意一处定义原则

注意实例化过程中的模板形参推导

1.10 函数模板的(完全)特化

特化:针对某种参数引入特别版的模板处理逻辑

注意与重载的区别

注意特化过程中的模板形参推导

避免使用模板特化

二.类模板与成员函数模板

2.1 template关键字引入模板

 类模板的声明与定义——翻译单元的一处定义原则

成员函数只有在被调用时才会被实例化

类内类模板名称的简写,便利而已

类模板成员函数的定义,分为类内和类外,void B<T>::fun()

2.2 成员函数模板

 类的成员函数模板:定义在类内部的函数模板 template <typename T> void B::fun()

类模板的成员函数模板

2.3 友元函数模板

 声明一个函数模板为某个类(模板)的友元

2.4 类模板的实例化

template class namespace::ClassName<xxT>; //实例化类内所有成员函数

template void namespace::ClassName<xxT>::mf(); //指定实现类内的某个成员函数

与函数实例化很像

可以实例化整个类,或者类中的某个成员函数

2.5 类模板的特化

2.6 类模板的实参推导

 

三.Concepts

C++20引入的重要特性

模板的问题:没有对模板参数引入相应的限制,如参数是否可以正常工作、编译报错友好型较差

说白了就是缺少类似CSharp中的泛型约束

3.1 C++20中引入

concept IsAvail = std::is_same_v<T,int> || std::is_same_v<T,float>;

编译器谓词,基于给定的输入,返回true或false

要求T是int或float

1 template <typename T>
2 concept IsAvail = std::is_same_v<T,int> || std::is_same_v<T,float>;
3 
4 template <typename T>
5     requires IsAvail<T>//模板约束
6 void fun(T input){
7     
8 }

(1)与constrains(require从句)一起使用限制模板参数

(2)通常置于表示模板形参的尖括号后面进行限制

3.2 定义与使用

1 template <typename T>
2 concept AddAble = requires (T a,T b){a+b;};
3 
4 template <AddAble T>
5 auto fun(T x,T y){
6     return x+y;
7 }

包含一个模板参数的Concept:

(1)使用require从句,事先定义好的requires表达式AddAble

(2)直接替换typename,template <IsAvail T> xxxxxx,很直观的约束

包含多个模板参数的Concept:

(1)用做类型constraint时,少传递一个参数,推导出的类型将作为首个参数

3.3 requires表达式

简单表达式:表明可以接收的操作

类型表达式:表明是一个有效的类型

复合表达式:表明操作的有效性,以及操作返回类型的特性

嵌套表达式:包含其它的限定表达式

注意区分requires从句与requires表达式:

requires从句,真正要用了

requires表达式,计算返回true或false

3.4 requires从句对重载解析的影响与特化版本的选取

 

四.模板相关内容

4.1 接收编译器常量/数值作为模板参数

 template <int a> class Str;

template <typename T, T value> class Str;

(C++ 17) template <auto value> class Str;

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

4.2 接收模板作为模板参数

 template <template<typename T> class C> class Str;

4.3 别名模板

可以使用using引入别名模板

(1)为模板本身引入别名

(2)为类模板的成员引入别名

(3)别名模板不支持特化,但可以基于类模板的特化引入别名,以实现类似特化的功能

4.4 变长模板

4.4.1 变长模板参数与参数包

形参包:

(1)类型...  template <int... a>,可以接收fun<1,2,3>();

(2)typename...  template <typename... a>,可以接收fun<int,double,char>()

(3)可以接收模板作为参数,

(4)其他略

4.4.2 sizeof...操作

获取形参包中参数个数,

sizeof...(T)会输出形参包中参数数量

4.4.3 变长模板参数的位置

4.5 包展开与折叠表达式

 包展开,将...展开成具体的参数类型,用逗号分隔

4.6 完美转发与lambda表达式模板

为了解决在传入左值、右值时可能因为模板的类型推导给偏离原意,需要一种保真的类型转发方式

模板中的T&&是万能引用

完美转发:std::forward函数,自C++11引入

(1)通常与万能引用结合使用,

(2)同时处理传入参数是左值或右值的情形

 1 #include <iostream>
 2 #include <utility>
 3 
 4 void g(int&){
 5     std::cout << "l-reference\n";
 6 }
 7 void g(int&&){
 8     std::cout << "r-reference\n";
 9 }
10 
11 template <typename T>
12 void fun(T&& input)//T&& 万能引用
13 {
14     std::cout << "Hello\n";
15     g(std::forward<T>(input));
16 }
17 
18 int main(){
19 
20     int x = 3;
21     fun(x);
22     fun(3);
23 
24     return 0;
25 }

 

Lambda表达式模板,自C++20引入

 

 4.6 消除歧义与变量模板

posted @ 2023-03-02 20:12  啊原来是这样呀  阅读(40)  评论(0)    收藏  举报