类型区别基本概念
template<typename T>
void func(const T&abc){}
编译器推导T的类型,不仅仅看调用func(10)给的实参10的类型,还看形参abc的类型
万能引用的模板表现形式
template<typename T>
void func(T&& a){}
如果我们给func传递了左值,那么万能引用就变成了左值引用;如果我们给func传递了右值,那么万能引用就变成了右值引用
注意:T&&才是万能引用,T不是万能引用
万能引用资格的剥夺与辨认
(1)剥夺:const修饰词会剥夺一个引用成为万能引用的资格,会被打回原型成为右值引用
例:
template<typename T>
void func(const T&&a){}
注意:此时a的类型就是右值引用
(2)辨认
template<typename T>
class MyTest{
void my_func(T&& a){}
template<typename T2>
void func2_my(T2&& b){}
}
注意:&MyTest::my_func并没有类型的推断,类型推断发生在MyTest类实例化时,所有my_func的形参a的类型是右值引用,而不是万能引用。而func2_my有类型的推断,所以,func2_my的形参b的类型是万能引用
如何推导编译器推断的类型
template<typename T>
void my_func(T& tmprv){}
int i = 18;
const int j = i;
const int& k=i;
如果tmprv类型是个指针或引用(但不是万能引用):
| 调用my_func | T的类型 | tmprv的类型 |
|---|---|---|
| my_func(i) | int | int& |
| my_func(j) | const int | const int& |
| my_func(k) | const int | const int & |
注意:1.若实参是引用类型,则引用类型会被忽略掉,T不会被推导为引用类型。2.实参的const属性会成为类型模板参数T类型推导的组成部分,所以不用担心在my_func中能够修改原来有const属性的实参
template<typename T>
void my_func(const T& tmprv){}
int i = 18;
const int j = i;
const int& k=i;
如果tmprv多了个const
| 调用my_func | T的类型 | tmprv的类型 |
|---|---|---|
| my_func(i) | int | const int& |
| my_func(j) | int | const int& |
| my_func(k) | int | const int& |
注意:1.若实参是引用类型,则引用类型会被忽略掉,T不会被推导为引用类型。2.如果模板函数my_func的形参tmprv里边出现了const,则T中的const属性会被剥夺
template<typename T>
void my_func(T* tmprv){}
int i = 18;
const int j = i;
const int& k=i;
const int *pi=&i;
| 调用my_func | T的类型 | tmprv的类型 |
|---|---|---|
| my_func(&i) | int | int* |
| my_func(pi) | const int | const int* |
结论:如果tmprv中没有const,则实参中的const就会被带到T类型中去。如果tmprv有const,则T类型中不会带const。
template<typename T>
void my_func(T&& tmprv){}
int i = 18;
const int j = i;
const int& k=i;
如果tmprv类型是个万能引用
| 调用my_func | T的类型 | tmprv的类型 |
|---|---|---|
| my_func(i) | int& | int& |
| my_func(j) | const int& | const int& |
| my_func(k) | const int& | const int& |
| my_func(100) | int | int&& |
如果tmprv类型是个常规的传值方式传递
void testFunc(){}
template<typename T>
void my_func(T tmprv){}
int i = 18;
const int j = i;
const int& k=i;
char mystr[]="I Love Myself!";
const char* const point = mystr;
const char c_mystr[]= "I Love China!";
| 调用my_func | T的类型 | tmprv的类型 |
|---|---|---|
| my_func(i) | int | int |
| my_func(j) | int | int |
| my_func(k) | int | int |
| my_func(c_mystr) | const char* | const char * |
| my_func(point) | const char* | const char * |
| my_func(testFunc) | void(*)() | void(*)() |
注意:由于tmprv是新副本,所以,const属性不会传递进去。但是如果你传递是const char*或者const char[]数组,那这个const会被保留
void testFunc(){}
template<typename T>
void my_func(T& tmprv){}
const char c_mystr[]= "I Love China!";
| 调用my_func | T的类型 | tmprv的类型 |
|---|---|---|
| my_func(c_mystr) | const char[14] | const char(&)[14] |
| my_func(testFunc) | void(void) | void(&)(void) |
从而可以获取数组长度。看下例:
template<typename T,unsigned L1>
void my_func(T(&tmprv)[L1])
{
cout<<"数组的长度是"<<L1<<endl;
}