万能引用类型
从上一章如何查看类型推断中,我们发现经过模板的类型推导,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;
cout << "-------------------end----------------------" << endl;
}
int i = 18; //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 const&,tmprv =int const &
myfunc(k); //T=int const & tmprv=int const &
myfunc(100); //T=int tmprv=int&&
传值方式
如果实参是引用类型,则引用类型会被忽略,T不会被推导为引用类型
template<typename T>
void myfunc2(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 i = 18; //i的类型是int
const int j = i; //j的类型是const int
const int& k = i; //k的类型是const int&
myfunc2(i); //T=int tmprv=int
myfunc2(j); //T=int tmprv=int
myfunc2(k); //T=int tmprv=int
//如果实参是引用类型,则引用类型会被忽略,T不会被推导为引用类型,除非手工引用类型(不建议这样写)
int& m = i;
myfunc2(m);
myfunc2<int&>(m);
若实参是const,则const会被忽略,T不会被推导为const类型(毕竟产生的是新副本)
char mystr[] = "I Love China";
const char* const p = mystr;
//第一个const表示p指向的目标中的内容不能通过p改变
//第二个const表示p指向一个内容后,p不可再指向其他的内容(p不可以指向不同内容)
myfunc2(p);//T=char const *,tmprv=char const*
//这表示进入到函数模板内部后,tmprv指向的内容不能通过tmprv改变,但是tmprv可以指向其他内存地址
//也就是tmprv(p)的常量性被忽略了,二tmprv(p)所指向的内容的常量性会被保留
结论:如果传递的是const char* 或者const char[],这个const会被保留
传值方式的引申–std::ref与std::cref
当函数模板定义中使用传值方式,可以通过std::ref和std:;cref引用方式传递参数
template<typename T>
void myfunc3(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& tmpvaluec = tmprv;
tmpvaluec = 1200;
}
int m = 180;
myfunc3(std::ref(m));//std::ref和std::cref像对象包装器,编译器通过创建一个class std::reference_wrapper<T>类型的对象
cout << "m=" << m << endl; //m=1800
数组做实参
用数组名作为实参传递,通常情况下会退化为指针
template<typename T>
void myfunc4(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;
}
const char mystr[] = "l love china";
myfunc4(mystr); //T=char const* tmprv=char const*
如何得到完整的类型,一个数组,其大小都属于数组类型一部分
template<typename T>
void myfunc5(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;
}
const char mystr[] = "l love china";
myfunc5(mystr); //T = char const [13]
如何得到数组大小
template<typename T,unsigned L1>
void myfunc6(T(& tmprv)[L1]) {
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;
cout << L1 << endl;//13
}
const char mystr[] = "l love china";
//tmprv = char const (&)[13] --(&)代表数组的一个引用
myfunc6(mystr);
函数名做实参
template<typename T>
void myfunc7(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;
}
myfunc7(testFunc);
//T = void __cdecl(void)
//tmprv = void(__cdecl&)(void)--tmprv是一个函数引用类型
初始化列表做实参
利用#include<initializer_list>
template<typename T>
void myfunc9(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;
cout << "-------------------end----------------------" << endl;
}
myfunc9({ 1,2,3 });
//T = int
//tmprv = class std::initializer_list<int>
总结
-
推断中,引用类型实参的引用类型等于不存在
-
万能引用,实参为左值或者右值,推断结果不一样
-
按值传递的实参,传递给形参时const属性不起作用,但传递是引用则另当当别论
-
数组或者函数类型再类型推断中会被看做是指针,除非函数模板的形参是引用。
-
初始化列表不能直接推断成std::initializer_list,必须在函数模板中明确这种类型