函数模板(参考《C++ Templates 英文版第二版》)
C++模板编程(参考《C++ Templates 英文版第二版》)
Chapter 1 函数模板
1.1 一窥函数模板
template<class T>
T max(T a, T b)
{return b < a ? a : b;
}
#include "max1.hpp"
#include <iostream>
#include <string>
#include <format>int main(int argc, char* argv[])
{int i = 42;std::cout << std::format("max(7,i):{}\n", ::max(7, i)); // C++20double f1 = 3.4;double f2 = -9.3;std::cout << std::format("max(f1,f2):{}\n", ::max(f1, f2));std::string s1 = "i am very intelligent";std::string s2 = "i am intelligent";/** 如果不使用::限定max为全局* 则默认使用std::max(),对于在std空间内的类型*/std::cout << std::format("max(s1,s2):{}\n", ::max(s1, s2)); }
输出:
max(7,i):42
max(f1,f2):3.4
max(s1,s2):i am very intelligent
模板不会被编译成一个可以处理任何类型的实例,而是不同的类型使用模板时,模板会生成不同的实例
通过确定的参数代替模板参数的过程叫实例化
1.1.3 两阶段Translation
为了一个不支持相应操作的类型实例化模板会导致错误
例如,复数比较大小
因此,模板的"编译"分两步:
- 在没有实例化前:
- 检查语法错误
- 使用不依赖模板参数的名字
- 检查不依赖于模板参数的静态断言
- 在实例化阶段,模板代码被检查是否有效,所有依赖于模板的代码都将被检查
template <typename T>
void f(T x) {undeclared(); // first-phase compile-time error if undeclared() unknownundeclared(x); // second-phase compile-time error if undeclared(T) unknownstatic_assert(sizeof(int) > 10,"int too small"); // always fails if sizeof(int) <= 10static_assert(sizeof(T) > 10,"T too small"); // fails if instantiated for T with size <= 10
}
有些编译器不进行完全的第一阶段检查,你有可能观察不到上述现象
1.2 函数参数推断(Template Argument Deduction)
当我们调用函数模板时,模板参数将由我们传入的参数确定
但是,T
可能只是参数的"一部分",例如,我们我们使用常量引用声明max()
template<typename T>
T amx(T const& a,T const& b)
{return a < b ? b : a;
}
在类型推断中的类型转换
在类型推断中类型转换是有限的
- 当使用引用传参时,参数类型必须完全匹配,编译器不会进行任何微小的转换
- 当使用值传参时,支持退化的转换,
const
voaltile
限定符会被忽略,引用转换为引用类型,数组,函数将会被转换成指针类型,两个参数退化(std::decayed
)1后的类型必须匹配
template <typename T>
T max(T a, T b) {return b < a ? a : b;
}int a = 1;
const int b = 42;
int& c = a;
int arr[4];
std::string s;::max(a, b); // OK:T 推断为 int
::max(b, b); // OK:T 推断为 int
::max(a, c); // OK:T 推断为 int
::max(&a, arr); // OK:T 推断为 int*
::max(1, 3.14); // 错误:T 推断为 int 和 double
::max("hello", s); // 错误:T 推断为 const char[6] 和 std::string
有三个方法解决这个问题:
强制转换参数使他们都匹配
std::cout << std::format("max1(1,4.4):{}", ::max(static_cast<double>(1), 4.4));
显示指定参数类型
std::cout << std::format("max1(1,4.4):{}", ::max<double>(1, 4.4));
默认参数的类型推断
template<typename T>
void f(T = "")
{}
f(1); // OK:T 推断为 int,调用 f<int>(1)
f(); // 错误:不能推断 T
如果想要支持这个例子,你也可以声明一个默认参数类型
f(); // OK
1.3 多种参数模板
函数模板有两种不同的参数
template parameters 模板参数
, 尖括号里面的就是模板参数call parameters 调用参数
,就是参数列表里面的template<typename T> // 模板参数 T max(T a,T b) // a,b就是调用参数
你可以使用多个模板参数
template<typename T1,typename T2> T1 max(T1 a,T2 b) {return a < b ? b : a; }
这个例子有一些问题,虽然他很有可能正常工作,但是他可能违背程序员的意愿,将返回值强制转换成T1,例如 double->int
有三种方式可以避免这个问题:
- 再加一个模板参数作为返回值类型
- 让编译器清楚返回值类型
- 把返回值声明成两个模板参数的共同类型
我们接下来讨论:
1.3.1 模板参数作为返回值
我们可以明确的指定参数类型
template<typename T> T max(T a, T b) {return a < b ? b : a; } ::max<int>(1,2.2);
但是很多情况下,参数与返回值类型并没有关系,我们就可以声明第三个模板参数作为返回值
template<typename T1, typename T2,typename RT> RT max(T1 a, T2 b) {return a < b ? b : a; }
但是模板参数推断并不会考虑到返回值,所也这么做是不行的,除非你手动指定返回值类型
::max<int,double,double>(1,3.3)
显然,这样做并没有什么优势
1.3.2 推断返回值
template<typename T1,typename T2> auto max(T1 a,T2 b) {return a < b ? b : a; }
auto
使得真正返回值必须从函数体中推断出来然而,在一些情况下,函数传入参数是引用类型,你想返回值类型,这时你就需要使用
std::decay
1去掉修饰符template<typename T1, typename T2> auto max(T1 a, T2 b) ->typename std::decay<decltype(true?a:b)>::type {return a < b ? b : a; }
1.3.3 返回公共类型(Common Type)
上面的例子可以直接这么写
#include <type_traits>template<typename T1, typename T2> std::common_type_t<T1,T2> max(T1 a, T2 b) {return a < b ? b : a; }
1.4 默认模板参数
你也可以定义默认的模板参数
我们可以使用之前的模板参数
template<typename T1, typename T2,typename RT = std::decay_t<decltype(true?T1():T2())>> RT max(T1 a, T2 b) {return a < b ? b : a; }
std::decay_t
确保不会返回引用我们可以使用
std::common_type_t<>
与std::decay
相同的效果template<typename T1, typename T2, typename RT =std::common_type_t<T1,T2>> RT max(T1 a, T2 b) {return a < b ? b : a; }
1.5 函数模板重载
// maximum of two int values:
int max (int a, int b)
{return b < a ? a : b;
}// maximum of two values of any type:
template<typename T>
T max (T a, T b)
{return b < a ? a : b;
}int main()
{::max(7, 42); // calls the nontemplate for two ints::max(7.0, 42.0); // calls max<double> (by argument deduction)::max('a', 'b'); // calls max<char> (by argument deduction)::max<>(7, 42); // calls max<int> (by argument deduction)::max<double>(7, 42); // calls max<double> (no argument deduction)::max('a', 42.7); // calls the nontemplate for two ints
}
总之,确保只有一个函数匹配
#include <cstring>
#include <string>// maximum of two values of any type:
template<typename T>
T max (T a, T b)
{return b < a ? a : b;
}// maximum of two pointers:
template<typename T>
T* max (T* a, T* b)
{return *b < *a ? a : b;
}// maximum of two C-strings:
char const* max (char const* a, char const* b)
{return std::strcmp(b,a) < 0 ? a : b;
}int main ()
{int a = 7;int b = 42;auto m1 = ::max(a,b); // max() for two values of type intstd::string s1 = "hey";std::string s2 = "you";auto m2 = ::max(s1,s2); // max() for two values of type std::stringint* p1 = &b;int* p2 = &a;auto m3 = ::max(p1,p2); // max() for two pointerschar const* x = "hello";char const* y = "world";auto m4 = ::max(x,y); // max() for two C-strings
}
如果用传引用实现模板,再用传值重载 C 字符串版本,不能用三个实参版本计算三个 C 字符串的最大值
#include <cstring>// maximum of two values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b)
{return b < a ? a : b;
}// maximum of two C-strings (call-by-value)
char const* max (char const* a, char const* b)
{return std::strcmp(b,a) < 0 ? a : b;
}// maximum of three values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b, T const& c)
{return max (max(a,b), c); // error if max(a,b) uses call-by-value
}int main ()
{auto m1 = ::max(7, 42, 68); // OKchar const* s1 = "frederic";char const* s2 = "anica";char const* s3 = "lucas";auto m2 = ::max(s1, s2, s3); // run-time ERROR
}
错误原因是 max(max(a, b), c)
中,max(a, b)
产生了一个临时对象的引用,这个引用在计算完就马上失效了
重载版本必须在函数调用前声明才可见
第一章结束
**我所有的学习笔记都在Github仓库里:https://github.com/fanxing-6/CPP-learning-notes **
,如果访问Github有问题也可以访问Gitee:CPP-learning-notes: C++学习笔记 (gitee.com)
本人能力有限,笔记难免有疏漏,如果有错误,欢迎各位关注公众号与我交流,一定会及时回复
Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer implicit conversions to the type
T
, removes cv-qualifiers, and defines the resulting type as the member typedeftype
. Formally:IfT
names the type "array ofU
" or "reference to array ofU
", the member typedeftype
is U*.Otherwise, ifT
is a function typeF
or a reference thereto, the member typedeftype
is std::add_pointer::type.Otherwise, the member typedeftype
is std::remove_cv<std::remove_reference::type>::type.These conversions model the type conversion applied to all function arguments when passed by value.The behavior of a program that adds specializations fordecay
is undefined. ↩︎ ↩︎
函数模板(参考《C++ Templates 英文版第二版》)相关推荐
- 非类型模板参数(参考《C++ Templates 英文版第二版》)
非类型模板参数(参考<C++ Templates 英文版第二版>) Chapter 3 3.1 非类型类模板参数 与前几章的简单例子不同,你也可以通过std::array实例化一个固定大小 ...
- 类模板(参考《C++ Templates 英文版第二版》)
类模板(参考<C++ Templates 英文版第二版>) Chapter 1 类模板 与函数相似,类也可以被一个或者多个类型参数化 在这章,我们使用栈作为例子 2.1 类模板stack的 ...
- 可变参数模板(参考《C++ Templates 英文版第二版》)
可变参数模板(参考<C++ Templates 英文版第二版>) Chapter 4 可变参数模板 自从C++11,模板可以接受可变数量的参数 4.1 可变参数模板 可以定义模板,去接受无 ...
- 利用python进行数据分析第二版pdf百度云_参考《利用Python进行数据分析(第二版)》高清中文PDF+高清英文PDF+源代码...
第2版针对Python 3.6进行全面修订和更新,涵盖新版的pandas.NumPy.IPython和Jupyter,并增加大量实际案例,可以帮助高效解决一系列数据分析问题. 第2版中的主要更新了Py ...
- 《数据结构与算法》第二版-陈卫卫-陆军工程大学811数据结构教材 第1-2章 参考答案
<数据结构与算法>(第二版)陈卫卫-高等教育出版社 陆军工程大学811数据结构教材 第1-2章 参考答案 习题1.1 1.1-1 (1)名称.数量.特征.性质的 ...
- 《流畅的Python第二版》读书笔记——函数作为一等对象
引言 这是<流畅的Python第二版>抢先版的读书笔记.Python版本暂时用的是python3.10.为了使开发更简单.快捷,本文使用了JupyterLab. 函数是Python的一等( ...
- 《lua程序设计(第二版)》学习笔记(五)-- 函数基础
-- 第 5 章 函数-- 一种对语句和表达式进行抽象的主要机制 print(os.date()); -- 打印日期 Sun Apr 20 12:44:46 2014 -- 一看到sun,感慨广州没有 ...
- 标准模板库之容器-《C++标准库(第二版)》读书笔记
写在前面:本文是阅读<C++标准库(第二版)>的读书笔记. 文章目录 6.1 STL组件(Component) 6.2 容器(Container) 6.2.1 序列式容器(Sequence ...
- 《汇编语言程序设计教程》人民邮电出版社第二版习题及参考答案
网上的答案是第一版的,重新整理了一下 <汇编语言程序设计教程>人民邮电出版社第二版 刘慧婷,王庆生 主编 习题及参考答案 更多汇编内容请访问:omegaxyz.com 第一章至第五章 核对 ...
最新文章
- web项目启动时 初始化加载系统参数 获取无法自动注入的服务
- 数据库学习day_03:关联关系/ 关联查询/ JDBC
- 使用Code First Migrations依据代码更新数据库结构
- ug区域轮廓铣没有重叠距离_UG数控加工编程 _固定轴、可变轴曲面轮廓铣图文详解...
- csv文件设置每个cell大小_Python对文本文件和Excel的处理机制
- 【渝粤教育】电大中专职业应用写作作业 题库
- RTMP流媒体播放过程
- linux mysql libc.so_mysql-arm-linux-gcc编译报错:libc.so format not recognized.
- 计算机前端学哪些好学,Web前端能干什么工作,好学吗
- C/C++学校运动会管理系统
- 《Python程序设计(第3版)》课后习题答案
- jade 支持html,Vue篇之vue 使用Jade模板写html
- 从拉格朗日插值法到范德蒙行列式
- Echart使用,看了就会的
- 前端开发环境搭建(工具使用)
- [2]rubyruby on rails入门笔记---Ruby中的异常
- CALayer的简单使用
- 头条限流是什么原因_头条号跨领域了,限流了,怎么补救!
- 中国工商银行网上银行B2C在线支付接口说明
- 怎么使用大疆无人机建模?
热门文章
- java map 随机取值_随机获取一个集合(List, Set)中的元素,随机获取一个Map中的key或value...
- 【RK3399Pro学习笔记】十五、ROS中launch启动文件的使用方法
- 【RK3399Pro学习笔记】十一、ROS服务数据的定义与使用
- 终极结束进程方法API
- Linux 系统应用编程——标准I/O
- 正则表达式:去掉数字(整数、小数)前面多余的零
- [ECMAScript] 你喜欢es6的哪些特性?
- 重学java基础第十五课:java三大版本
- Taro+react开发(8)--控制跳转
- 前端学习(2948):webpack创建简单项目