【C++ Primer 第16章】2. 模板实参推断
模板实参推断:对于函数模板,编译器利用调用中的函数实参来确定模板参数,从函数实参来确定模板参数的过程被称为模板实参推断。
类型转换与模板类型参数
与往常一样,顶层const无论在形参中还是在是实参中,都被会忽略。
• const转换:可以将一个非const对象的引用(或指针)传递给const的引用(或指针)形参。
• 数组或函数指针转换:一个数组实参可以转换为一个指向其首元素的指针。类似的,一个函数实参可以抓转换一个该函数类型的指针。
template <typename T> T fobj(T, T) //实参被拷贝
template <typename T> T fret(const T&, const T&) //引用 string s1("a value");
const strng s2("another value");
fobj(s1, s2); //调用fobj(string, string); const被忽略
fret(s1, s2); //调用fret(const string&, const string&); const被忽略 int a[], b[];
fobj(a, b); //调用fobj(int*, int*);
fret(a, b); //错误
使用相同的模板参数类型的函数形参
一个模板类型形参可以用作多个函数形参的类型,由于只允许有限的几种类型转换,因此传递给这些形参的参数必须具有相同的类型。
template <typename T>
int compare(const T& v1, const T& v2)
{
if (v1 < v2) return -;
if(v2 > v1) return ;
return ;
} long lng;
compare(lng, ); //错误:不能实例化compare(long, int); template <typename A, typename B>
int compare(const A& v1, const B& v2) //实参类型不一样,但必须兼容
{
if (v1 < v2) return -;
if(v2 > v1) return ;
return ;
}
正常类型转化应用于普通函数实参
函数模板可以有用普通类型定义的参数,即,不涉及模板类型参数的类型,这种函数实参不进行特殊处理。
template <typename T>
ostream &print(ostream *os, const T &obj)
{
return os << obj;
} print(cout, ); //使用print(ostream &os, const int &obj);
ofstream f("output");
print(f, ); //使用print(ostream &os, const int &obj); 将f转换为ostream&
函数模板显示实参
在C++中,若函数模板返回类型需要用户指定,那么在定义函数模板时,模板参数的顺序是很重要的,如下代码:
template <typename T1, typename T2, typename T3> //模板一
T1 sum(T2 a, T3 b)
{
return a + b;
}
在调用的时候就需要指定T1的类型,如:sum<float>(1,2);于是sum函数的返回类型为float。
但是有时候由于设计者的糟糕设计,会导致一些问题:
template <typename T1, typename T2, typename T3> //模板二
T3 sum(T1 a, T2 b)
{
return a + b;
}
那么再进行上述调用,则会出现问题:sum<float>(1, 2);现在这个调用里指定T1类型为float,但是实际传进来的是1(int类型),会进行隐式类型转换,将1转换为float;T2的类型也可以根据sum(1,2)调用的第二个实参推断出来,这里是可能会是int。那么T3是什么类型呢?显然这里编译器无法推断T3的类型,需要在调用时指定才能推断:
sum<int, int, int>(1, 2),这样T3就推断出来是int。
在指定显示模板实参时指定的类型是和模板参数匹配的,顺序是一一对应的,如:
- 使用sum<int>(1, 2)对上面的第一个模板进行调用,那么T1对应int,T2和T3则通过推断得出。
- 使用sum<int>(1, 2)对上面的第二个模板进行调用,那么T1对应的类型是int,T2可以根据实际穿进去的参数进行推断,这里2为int那么T2类型就是int,那么编译器就无法知道T3的实际类型了。
尾置返回类型与类型转换
函数指针和实参推断
转发
某些函数需要将一个或多个实参连同类型不变地转发给其他函数。在次情况下,我们需要保持被转发实参的所有性质,包括实参类型是否是const的以及实参是左值还是右值。
//接受一个可调用对象和另外两个参数的模板
template <typename F, typename T1, typename T2>
void flips(F f, T1 t1, T2 t2)
{
f(t2, t1)
} void f(int v1, int &v2) //注意v2是一个引用
{
cout << v1 << " " << ++t2 << endl;
} f(, i); //f改变了实参i
flip1(f, j, ); //通过flip1调用不会改变j
// void flip1(void(*fcn)(int, int&), int ti, int t2);
定义能保持类型信息的函数参数
template <typename F, typename T1, typename T2>
void flips(F f, T1 &&t1, T2 &&t2)
{
f(t2, t1)
} void f(int v1, int &v2) //注意v2是一个引用
{
cout << v1 << " " << ++t2 << endl;
} flip1(f, j, );
template <typename F, typename T1, typename T2>
void flips(F f, T1 &&t1, T2 &&t2)
{
f(t2, t1)
} void g(int &&v1, int &v2)
{
cout << v1 << " " << ++t2 << endl;
} flip1(g, j, ); //错误:不能从一个左值实例化int&&
在调用中使用std::forward保持类型信息
• forward定义在头文件utility中,与move不同,forward必须显式模板来调用。
• foeward返回该类型显式实参类型的右值引用,即,forward<T>的返回类型是T&&。
template <typename F, typename T1, typename T2>
void flips(F f, T1 &&t1, T2 &&t2)
{
f(t2, t1)
} void g(int &&v1, int &v2)
{
cout << v1 << " " << ++t2 << endl;
} flip1(f, j, ); //错误:不能从一个左值实例化int&& 修改为: template <typename F, typename T1, typename T2>
void flips(F f, T1 &&t1, T2 &&t2)
{
f(std::forward<T2> (t2), std::forward<T1> (t1))
} void g(int &&v1, int &v2)
{
cout << v1 << " " << ++t2 << endl;
} flip1(f, j, ); //i将以int&类型传递给g,42将以in&&类型传递给g
【C++ Primer 第16章】2. 模板实参推断的更多相关文章
- [C++ Primer] : 第16章: 模板与泛型编程
面向对象编程(OOP)和泛型编程都能处理在编写程序时不知道类型的情况, 不同之处在于: OOP能处理类型在程序运行之前都未知的情况, 而在泛型编程中, 在编译时就能获知类型了. 函数模板 模板是C++ ...
- C++ Templates (1.2 模板实参推断 Template Argument Deduction)
返回完整目录 目录 1.2 模板实参推断 Template Argument Deduction 1.2 模板实参推断 Template Argument Deduction 当调用函数模板(如max ...
- C++学习笔记(4)----模板实参推断
1. 如图所示代码,模板函数 compare(const T&, const T&) 要求两个参数类型要一样. compare("bye","dad&qu ...
- 【C++ Primer 第16章】1. 定义模板 (一)
类模板 #include<iostream> #include<vector> #include<memory> using namespace std; temp ...
- 【c++ Prime 学习笔记】第16章 模板与泛型编程
面向对象编程(OOP)和泛型编程(GP)都能处理在编写程序时类型未知的情况 OOP能处理运行时获取类型的情况 GP能处理编译期可获取类型的情况 标准库的容器.迭代器.算法都是泛型编程 编写泛型程序时独 ...
- C++ primer plus读书笔记——第16章 string类和标准模板库
第16章 string类和标准模板库 1. string容易被忽略的构造函数: string(size_type n, char c)长度为n,每个字母都为c string(const string ...
- C++ Primer 5th 第16章 模板与泛型编程
模板是C++中泛型编程的基础,一个模板就是创建一个类或者函数的蓝图或者说公式. C++模板分为函数模板和类模板. 类模板则可以是整个类是个模板,类的某个成员函数是个模板,以及类本身和成员函数分别是不同 ...
- 《C++ Primer Plus》16.3 标准模板库 学习笔记
STL提供了一组表示容器.迭代其.函数对象和算法的模板.容器是一个与数组类似的单元,可以存储若干个值.STL容器是同质的,即存储的值的类型相同:算法是完成特定任务(如对数组进行排序或在链表中查找特定值 ...
- Linux就这个范儿 第16章 谁都可以从头再来--从头开始编译一套Linux系统 nsswitch.conf配置文件
Linux就这个范儿 第16章 谁都可以从头再来--从头开始编译一套Linux系统 nsswitch.conf配置文件 朋友们,今天我对你们说,在此时此刻,我们虽然遭受种种困难和挫折,我仍然有一个梦 ...
随机推荐
- JAVA-大白话探索JVM-类加载器(一)
JVM??? Java语言的一个非常重要的特点就是与平台的无关性.而使用Java虚拟机是实现这一特点的关键.JVM是Java Virtual Machine(Java虚拟机)的缩写,Java程序编译后 ...
- vue项目首次加载过慢
vue项目优化 浅谈 Vue 项目优化 关于vue在app首次加载缓慢的解决办法 nginx开启缓存 在http部分加入 #要想开启nginx的缓存功能,需要添加此处的两行内容! #设置Web缓存区名 ...
- castle动态代理的使用
转自:https://blog.csdn.net/educast/article/details/6565447#动态代理的原理 原理其实很简单,就是在运行时生成新的对象,姑且叫做T,并使T继承自需要 ...
- MySQL - 日常操作二 备份还原
登录mysql的命令 # 格式: mysql -h 主机地址 -u 用户名 -p 用户密码 mysql -h 110. -P3306 -uroot -p mysql -uroot -p -S /dat ...
- Android如何降低service被杀死概率
http://www.jianshu.com/p/06a1a434e057 http://www.cnblogs.com/ylligang/articles/2665181.html Android应 ...
- DSO windowed optimization 代码 (3)
4 Schur Complement 部分信息计算 参考<DSO windowed optimization 公式>,Schur Complement 部分指 Hsc(\(H_{X\rho ...
- GNU/Linux的GNU是什么意思
这个组织中黑客云集,而且多是掌握核心技术的真正高手,他们的作品多是编译器.词法/语法分析器.底层函数库等大作.更重要的不是他们的技术,而是他们的哲学!他们的哲学就是技术上的“共产主义”——人人为我,我 ...
- 详解Jquery选择器
1.常见的选择器 id,类,标签选择器. $("#a1") $(".myclass") $("div") 2.组合选择器 $("# ...
- 【转】inotify+rsync实现实时同步
[转]inotify+rsync实现实时同步 1.1 什么是实时同步:如何实现实时同步 要利用监控服务(inotify),监控同步数据服务器目录中信息的变化 发现目录中数据产生变化,就利用rsync服 ...
- caffe源码阅读(1)_整体框架和简介(摘录)
原文链接:https://www.zhihu.com/question/27982282 1.Caffe代码层次.回答里面有人说熟悉Blob,Layer,Net,Solver这样的几大类,我比较赞同. ...