万能引用类型

从上一章如何查看类型推断中,我们发现经过模板的类型推导,T的类型是不完整的,我们想得到完全的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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不会被推导为引用类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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类型(毕竟产生的是新副本)

1
2
3
4
5
6
7
8
9
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引用方式传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

数组做实参

用数组名作为实参传递,通常情况下会退化为指针

1
2
3
4
5
6
7
8
9
10
11
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*

如何得到完整的类型,一个数组,其大小都属于数组类型一部分

1
2
3
4
5
6
7
8
9
10
11
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]

如何得到数组大小

1
2
3
4
5
6
7
8
9
10
11
12
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);

函数名做实参

1
2
3
4
5
6
7
8
9
10
11
12
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

1
2
3
4
5
6
7
8
9
10
11
12
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>

总结

  1. 推断中,引用类型实参的引用类型等于不存在

  2. 万能引用,实参为左值或者右值,推断结果不一样

  3. 按值传递的实参,传递给形参时const属性不起作用,但传递是引用则另当当别论

  4. 数组或者函数类型再类型推断中会被看做是指针,除非函数模板的形参是引用。

  5. 初始化列表不能直接推断成std::initializer_list,必须在函数模板中明确这种类型