区别定义与声明
T是模板形参 int是模板实参
inpunt是函数形参 3是函数实参
显示实例化
模板必须实例化可见 翻译单元一处定义原则
与内联函数异同
引入原因:函数模板是为了编译器两个阶段的处理 内联函数是为了能在编译期展开
推导规则示例
1.
2.万能引用:函数模板的 T&&是万能引用
引用折叠: 当有两个引用相互绑定时,它们会被折叠成一个引用。在模板实例化期间,引用折叠规则被应用于模板参数。
const
即使相关,也不一定能进行推导,推导成功也可能存在因歧义而无法使用
但是缺省只能处理无法推导的情况 不能处理能推导但是有歧义的情况
模板缺省实参和函数缺省实参区别
函数缺省实参要从右向左设置。 模板可以不是
1.SPFINAE
函数模板中的替换失败(Substitution Failure Is Not An Error,简称 SFINAE)是一种编译器处理模板实例化失败的机制。当尝试实例化一个模板时,如果由于某些原因导致实例化失败,编译器并不会报错,而是会尝试使用备选的模板或者进行其他处理。
报错:没有匹配的函数 、忽视函数
重载函数 匹配失败就会找其他模板实例化 忽略掉前一个
2.模板与非模板同时匹配,匹配等级相同,此时选择非模板的版本
下面的代码调用了非模板的fun函数
注意 是匹配等级相同的情况才会选择非模板 如果不同 则会选择更加完美的匹配
下面的代码会调用模板fun
3.多个模板同时匹配,此时采用偏序关系确定选择最特殊的版本
float更加特殊 所以匹配第二个fun
如果同样特殊就会报错
标准类型转换模板
尾置返回类型与类型转换
template void fun(int)
/ template void fun(int)
extern template void fun(int)
/ extern template void fun(int)
template<> void f(int)
/ template<> void f(int)
1.if constexpr解决
2.假函数解决
函数模板不能偏特化
成员函数只有在调用时才会被实例化
证明:程序无法编译 但是去掉21行就能编译
类模板成员函数类外定义
类的成员函数模板
类模板的成员函数模板
类内定义
类外定义
fun是个友元函数模板
成员对象只有类实例化的时候才会被实例化
static函数只有使用的时候才会实例化
C++11模板参数为友元
类模板的显示实例化
完全特化
偏特化
B x(3)自动推导成B
pair的自动推导
C++17之前的解决方法 函数模板推导
vector
)Concepts
:编译期谓词,基于给定的输入,返回true
或false
constraints
(require
从句)一起使用时限制模板参数此处限制T是int和float
优点:报错一目了然
Concept
的定义与使用
Concept
requires
从句typename
Concept
constraint
时,少传递一个参数,推导出的类型将作为首个参数requires 表达式 (C++20 起) - cppreference.com 参考资料
requires
从句所引入的限定具有偏序特性,系统会选择限制最严格的版本如下 C1更严格 编译器选择匹配C1
A||B
”进行限制,之后分别针对A
与B
引入特化模板需要编译两次,在第一次编译时仅仅检查最基本的语法,比如括号是否匹配。等函数真正被调用时,才会真正生成需要的类或函数。
所以这直接导致了一个结果,就是不论是模板类还是模板函数,声明与实现都必须放在同一个文件中。因为在程序在编译期就必须知道函数的具体实现过程。如果实现和声明分文件编写,需要在链接时才可以看到函数的具体实现过程,这当然会报错。
于是人们发明了.hpp文件来存放模板这种声明与实现在同一文件的情况。
模板可以接受编译器常量为模板参数
C++17auto value
C++20支持浮点数作为模板参数(clang 12不支持)
C++17模板的模板考虑缺省实参
clang12支持有限
变长模板(Variadic Template)
形参包(parameter pack)是C++中用于处理可变数量参数的一种特性。形参包可以接受任意数量的模板参数,并在模板中进行处理。
形参包的基本语法是使用...
来表示,可以用在函数模板、类模板以及别的模板上下文中。形参包的展开可以通过递归、折叠表达式(C++17引入)等方式进行。
接数值
接类型
带可选名字的函数形参包
右值引用失效
使用万能引用 T&&情况
还是失效 原因:右值引用是个左值
解决方法:转发 forward
std::forward
函数
internal*p被编译器解读成乘法
typename
与template
消除歧义
typename
表示一个依赖名称是类型而非静态数据成员使用template
表示一个依赖名称是模板
使用template
表示一个依赖名称是模板
template
与成员函数模板调用
internal被解读为依赖obj <被解读成小于号
解决方法
template T pi = (T)3.1415926;
C++14 引入了变量模板(Variable Templates)的概念,它允许你定义参数化的变量,类似于函数模板允许你定义参数化的函数。变量模板提供了一种通用的方式来定义与类型相关的常量或变量,使得代码更具有通用性和灵活性。
// 使用 C++20 中的 lambda 模板
auto genericLambda = [](T x, T y) {
return x + y;
};
#include
#include
#include
#include
template
struct get_type{
using type=T;
};
template
struct get_type{
using type=T;
};
template
class myArray{
using iterator=T*;
using const_iterator=const T*;
public:
myArray(size_t count);
~myArray();
myArray(const std::initializer_list& list);
myArray(std::initializer_list& list);
iterator begin(){
return _data;
}
const_iterator cbegin()const{
return _data;
}
private:
T* _data;
};
template
myArray::myArray(size_t count) {
if(count){
_data=new T[count];
}
else{
_data= nullptr;
}
}
template
myArray::~myArray() {
if (_data) {
delete[] _data;
}
}
template
myArray::myArray(const std::initializer_list& list){
if(int count=0;list.size()){
_data=new T[list.size()]();
if constexpr (std::is_pointer_v){ //萃取 如果是指针就要深拷贝
for(auto e:list)
_data[count++]=new typename get_type::type(*e);
}
else{
for(const auto& e:list){
_data[count++]=e;
}
}
}
else{
_data= nullptr;
}
}