引子
模板类型推导是Modern C++特性auto的基础,但模板类型推导和auto类型推导有一些区别,具体看正文吧!
正文
先看这样一段代码
template<typename T>
void f(ParamType param)
以及调用
f(expr)
在调用时,编译器会根据expr推导两个类型:一个是T,一个是ParamType。这两个类型通常是不同的,因为ParamType会包含一些修饰,比如:const或引用修饰符。如果模板声明形式如下:
template<typename T>
void f(const T& param)
同时有,
int x = 0;
f(x);
那么T被推到成int类型,ParamType被推到成const int&类型。因此,类型推导不仅依赖T,还要依赖ParamType。总的来说,分以下三种情况:
- ParamType是指针或引用类型,但不是通用引用(通用引用将在Item 24中详细描述)。
- ParamType是通用引用类型。
- ParamType既不是指针也不是引用。
Case 1. ParamType是指针或引用类型,但不是通用引用
这是最简单的情况,类型推导过程如下:
- 如果expr是一个引用,忽略引用的部分。
- 然后用expr的类型与ParamType进行模式匹配,决定T的类型。
例如:
template<typename T>
void f(T& param);
int x = 27;
const int cx = x;
const int& rx = x;
那么类型推导结果如下:
f(x); // T是int,param的类型是int&
f(cx); //T是const int,param的类型是const int&
f(rx); //T是const int,param的类型是const int&
当参数是const引用时,模板推导出的参数也是const的。
模板类型推导会忽略引用。
现在我们假设param是一个const引用,那么T推导出的类型就不带const性质了:
template<typename T>
void f(const T& param);
int x = 27;
const int cx = x;
const int& rx = x;
f(x); //T是int,param的类型是const int&
f(cx); //T是int,param的类型是const int&
f(rx); //T是int,param的类型是const int&
同样,模板类型推导会省略引用。
指针的情况与引用是一样的。
Case 2. ParamType是通用引用类型
通用引用声明类似右值引用,形如T&&,当传入一个左值引用时,类型推导会稍有不同。
- 如果expr是一个左值,那么T和ParamType都会被推导为左值引用。这是模板类型推导中唯一会推导引用类型的情况。另外,尽管ParamType声明使用右值引用的语法,但它的推导类型是一个左值引用。
- 如果expr是一个右值,1中的推导规则适用。
例如:
template<typename T>
void f(T&& param);
int x = 27;
const int cx = x;
const int& rx = x;
f(x); //T是int&,param的类型是int&
f(cx); //T是const int&,param的类型是const int&
f(rx); //T是const int&,param的类型是const int&
f(27); //T是int,param的类型是int&&
使用通用引用作模板参数推导时,会区分参数是左值还是右值。
Case 3. ParamType既不是指针也不是引用
ParamType既不是指针也不是引用,那么就是值传递。
template<typename T>
void f(T param);
在这种情况下,由param对象决定类型推导规则:
- 如前所述,如果expr是一个引用,忽略引用部分。
- 如果expr是const的或volatile的,也要一起忽略。
因此:
int x = 27;
const int cx = x;
const int& rx = x;
f(x); //T是int,param的类型是int
f(cx); //T是int,param的类型是int
f(rx); //T是int,param的类型是int
考虑下面这中情况:
template<typename T>
void f(T param);
const char* const ptr = "Fun with pointers";
f(ptr);
传值会忽略顶层const,即新的指针对象本身可变,但其指向的内容不可变。
数组参数
char型数组和指针可以隐式转换。那么当数组作为模板参数传值时会发生什么。
cosnt char name[] = "J. P. Briggs";
const char* ptrToName = name;
template<typename T>
void f(T param);
f(name); //name is array, but T deduced as const char*
template<typename T>
void f(T& param);
f(name); // T deduced as const char (&)[13]
函数参数
C++中,函数也可以隐式转换为指针类型:
void someFunc(int, double); // someFunc is a function; type is void(int, double)
template<typename T>
void f1(T param); // in f1, param passed by value
template<typename T>
void f2(T& param); // in f2, param passed by ref
f1(someFunc); // param deduced as ptr-to-func; type is void (*)(int, double)
f2(someFunc); // param deduced as ref-to-func; type is void (&)(int, double)
总结
- 模板参数推导会忽略引用
- 使用通用引用参数推导类型时,对左值始终推导为左值
- 传值推导时,const和volatile也会被忽略
- 传值推到时,数组和函数都会被推导为指针类型;传引用则推导为本身的类型。
网友评论