模板函数类型推断
2022-02-10 本文已影响0人
404Not_Found
- 作者: 雪山肥鱼
- 时间:20220208 23:41
- 目的: 理解函数模板类型推断
# 查看类型推断结果
# 理解函数模板类型推断
## 引用与指针类型
### 额外补充(& 类型 丢失)
### 形参带const
### 编码技巧总结
### 形参为指针类型
## 万能引用
## 值传递
### 值传递传入指针
## 值传递方式的引申 -- std::ref 于 std::cref
## 数组做实参
## 函数名做实参
## 初始化列表做实参
# auto 类型 常规推断
## 传值方式
## 指针/引用
## 万能引用
查看类型推断结果
通过查看编译器给我们推断的类型结果,来学习c++类型推断的规则
方法:利用 boost 库 查看类型推断
vs2017 添加 boost库 手段:
- 右击项目属性,选择包含目录,增加boost路径
$(VC_IncludePath);$(WindowsSDK_IncludePath);D:\tools\boost_1_55_0b1
- 形参是 const T &
利用boost 对 形参类型进行
boost版本:1.68
推断:
#include <iostream>
#include <vector>
#include <boost/type_index.hpp>
using namespace std;
//如何查看类型推断结果
template <typename T>
void myfunc(const T & tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
cout << "--------end------------" << endl;
}
int main(int argc, char **argv) {
//如何查看类型推断结果 -- 编译器给我们的推断结果
//以来 boost库
//结果:
//T = int
//tmprv = int const &
//myfunc形参的类型 不仅取决于100, 还取决于tmprv的类型(const T &)相关
myfunc(100);
return 0;
}
函数模板的形参取决于:
- 100 的类型 即 函数模板推断出的 <T> 类型
- 取决于 函数形参 tmprv 的类型 const T&
结果示意:
图片.png
- 形参是 引用 或者 指针
template <typename T>
void myfunc(T & tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
cout << "--------end------------" << endl;
}
int main(int argc, char **argv) {
int i = 10;// i: int
const int j = i;//j: const int
const int &k = i;//k: const int &
/*
1.
*/
myfunc(i);// T:int tmprv:int &
myfunc(j);// T:int const, tmprv:int const &
myfunc(k);// T: int const, tmprv: int const &
return 0;
}
图片.png
注意 第 三种情况, T 的 类型是 const int,并非 const int &
结论:
- 若实参是引用类型,那么引用部分会被忽略,typename T 并不会推导成 引用类型。依然是 int类型
- 当实参传入的是 const int 时, T被判定为 const int, tmprv 被判定为 const int &
额外补充:
根据上一个小节的结论,我们做一个测试.
void mfRef(int & tmprv) {
tmprv = 12;
}
template <typename T>
void mfRefT(T tmprv) {
tmprv = 12;
}
int main(int argc, char **argv) {
int ii = 1;
int & jj = ii;
//mfRef(jj);//12
mfRefT(jj);//1
cout << ii << endl;
return 0;
}
非常明显mfRefT 中的T, 虽然实参的类型是 int & ,但 T 仅仅识被判定成 int。所以虽然产出的引用,但是并没有改变ii的值。
如果想修改,非常简单:
template <typename T>
void mfRef(T & tmprv) {
}
形参带const
template <typename T>
void myfunc(const T & tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
cout << "--------end------------" << endl;
}
int main(int argc, char **argv) {
int i = 10;// i: int
const int j = i;//j: const int
const int &k = i;//k: const int &
myfunc(i);// T:int tmprv:int &
myfunc(j);// T:int, tmprv:int const &
myfunc(k);// T: int, tmprv: int const &
return 0;
}
图片.png
对比没有const:
T 被判定成了 int,竟然去掉了 const属性?
结论:
- 如果实际参数是引用,那么typename T 依旧只会被判定为原始类型.
- 形参中带 const 时,如果实参是const int or const int &, 则 T 的类型被判定为 int,并非是const int ,此处异于 形参不带const.所以,形参的类型实际上是影响着T的,所以 typename T,也受着形参类型的影响。
编码技巧总结
- 形参中引用作用
- 修改实参值
- 引用比传值效率高
- 有效考虑用 T & tmprv 处理
- 即想享用形参带来的高效,又不希望通过形参来修改原来的值,则形参使用 const T & tmprv 即可。则形参不会被修改。
形参为指针类型
template <typename T>
void myfunc(T* tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
cout << "--------end------------" << endl;
}
int main(int argc, char **argv) {
int i = 10;// i: int
const int * j = &i;//j: const int
myfunc(&i);//int,int *
myfunc(j);//int const, int const *
return 0;
}
图片.png
一切 同 引用相同
当 形参带了 const时:
template <typename T>
void myfunc(const T* tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
cout << "--------end------------" << endl;
}
int main(int argc, char **argv) {
int i = 10;// i: int
const int * j = &i;//j: const int
myfunc(&i);//int,int *
myfunc(j);//int, int const *
return 0;
}
图片.png
差异性和 引用是形参时相同。T 受到 形参加const 的影响,T丢失了 const 属性。为 int。
万能引用
//万能引用
template <typename T>
void myfunc(T &&tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
int main(int argc, char **argv) {
int i = 10;// i: int
const int j = i;//j: const int
const int &k = i;//k: const int &
//都是左值,注意这里 T 的类型!
myfunc(i);//int&, int &
myfunc(j);//const int &, const int &
myfunc(k);//const int &, const int &
myfunc(100);//int , int &&
return 0;
}
图片.png
注意形参是 万能引用时,因为传入的是左值,所以 T 这时候并不是单纯的int,而是 int&.
值传递
#if 1
//传值方式
template <typename T>
void myfunc(T tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
int main(int argc, char **argv) {
int i = 10;// i: int
const int j = i;//j: const int
const int &k = i;//k: const int &
myfunc(i);//int, int
myfunc(j);// int , int
myfunc(k);// int , int
return 0;
}
图片.png
- 值传递中,T 的类型 引用被忽略,全部传为 T
除非手动传入 引入
int &m = i
myfunc(m); //& 依旧被忽略
myfunc<int &>(m);//手动指定T 类型
图片.png
最好不要应用值传递
- typename T const 属性的丢失,因为毕竟产生的是新副本,原来的属性与我无关
值传递传入指针
char mystr[] = "I love china";
const char * const p = mystr;
myfunc(p);//const char *, const char *
图片.png
- 第一个const 被保留, 第二个const 属性丢失
值传递的 std:ref()/std:cref()
按章上一小节,因为传入的是副本,并不会影响实参值。
std::ref() 执行引用传递,函数内部修改形参后,则会影响实参。
std:cref() const 引用
当函数模板定义中使用传值方式,可以通过std::ref和std::cref 来引用方式传参。
template <typename T>
void myfunc(T tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
int main(int argc, char **argv) {
int m = 180;
myfunc(std::ref(m));//std::ref()/std::cref();是对象包装器,
/*
std::ref() - 对象的包装器(是一个模板函数,返回下面的一个类)
编译器动作:创建一个类型为
class std::reference_wrapper<T> 的对象
即 myfunc中传递的 就是 上述的一个对象。
*/
cout << "m = " << m << endl;
return 0;
}
注意上述注释中说法:
图片.png
如何修改值呢?
//在函数中增加:
int & tmpvaluec = tmprv;
tmpvaluec = 1200;
数组做实参
- 形参为 T
template <typename T>
void myfunc(T tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
int main(int argc, char **argv) {
const char mystr[] = "I love china";
myfunc(mystr); //const char *, const char *
return 0;
}
图片.png
- 形参为 T&
template <typename T>
void myfunc(T & tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
int main(int argc, char **argv) {
const char mystr[] = "I love china";
myfunc(mystr);//const char [14], const char (&) [14]
return 0;
}
图片.png
tmprv 被判定成了 const char (&)[14] 数组的引用。
如果写法如下:
template <typename T>
void myfunc(T(&)[L1]) {
cout<<"L1"<<L1<<endl;
}
函数名做实参
- 值传递
template <typename T>
void myfunc(T tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
void testFunc() {
}
int main(int argc, char **argv) {
myfunc(testFunc);
return 0;
}
图片.png
--__cdecl函数调用方式而已。接触到再详细搜索--
T: 函数指针类型
tmprv: 函数指针类型
- 传递引用方式
template <typename T>
void myfunc(T & tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
void testFunc() {
}
int main(int argc, char **argv) {
myfunc(testFunc);
return 0;
}
图片.png
tmprv: 变成一个函数引用 void(&)(void)
初始化列表做实参
#include <initializer_list>
template <typename T>
void myfunc(std::initializer_list<T> tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
int main(int argc, char **argv) {
myfunc({ 1,2,3 });
return 0;
}
注意tmprv的类型
图片.png
总结
- 推断类型,引用类型实参 被判定为 普通实参类型 int
- 万能引用类型,实参为左值, 右值 ,推断结果各不相同
- 按值传递的实参,传递给形参时,const 不起作用,但是指针则另当别论。
- 数组或函数类型,值传递推断中被判定为指针。引用传递时,则铃铛别论。
- 当传递的是初始化列表时,则需要名确指定形参类型为 intialized_list类型。
auto 类型常规推断
声明变量的始化根据变量初始化的类型,自动为此变量选择匹配类型。
特点:
- auto的自动类型推断发生在编译期间
- auto定义变量必须立即初始化,这样才能被推断出来。
- 编译期间用真正的类型替换掉auto
- 可以和指针 引用 const类型结合使用。
- auto推断出来后,会代表一个具体类型.
将auto 理解成 函数模板中的 typename T, auto类型的变量名,相当于函数模板的形参. 记住这句话就行了.
实际上还是有两层 auto 相当于T, 那么 x类型 相当于 函数模板形参类型
template <typename t>
void myfunc(std::initializer_list<t> tmprv) {
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "t = " << type_id_with_cvr<t>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
int main(int argc, char **argv) {
auto x = 27;//x = int, auto = int.
return 0;
}
auto 是一个类型, x也有一个类型.
传值方式
auto 后 直接接变量名.
int main(int argc, char **argv) {
auto x = 27;//x int, auto int
const auto x2 = x; //x2 const int, auto :int
const auto &xy = x;//xy const int, auto : int
auto xy2 = xy;//xy2:int(复制,不保留原来属性), auto int
using boost::typeindex::type_id_with_cvr;
cout << "xy2 = " << type_id_with_cvr<decltype(xy2)>().pretty_name() << endl;
return 0;
}
总结:
- 传值类型会抛弃 const 类型.因为是赋值拷贝
指针或者引用(非万能引用)
auto 后接一个 &, 以及如何验证auto类型
template <typename T>
void tf(const T & tmprv) {//xy 相当于 tmprv, auto 相当于 T
cout << "--------begin------------" << endl;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}
int main(int argc, char **argv) {
auto x = 27;//x int, auto int
const auto x2 = x; //x2 const int, auto :int
const auto &xy = x;//xy const int, auto : int
auto xy2 = xy;//xy2:int(复制,不保留原来属性), auto int
tf(xy);
return 0;
}
图片.png
测试 xy2
图片.png
auto y = new auto(100);//y:int *, auto:int *
const auto * xp = &x;// xp:const int, auto (其实就是T): int
auto *xp2 = &x; //xp2:int *, auto :int
auto xp3 = &x;//auto: int, xp3 :int*
总结:
- 传指针或者引用方式对auto类型,不会抛弃const限定,但是会抛弃& , 其实就是 typename T 抛弃引用.
万能引用
与函数模板相同
auto && test0 = 222; //auto:int, test0: int&&;
auto && test1 = x; // auto:int, test1:int&