模板类型推导
template<typename T>
void f(ParamType param)
f(expr)
T的类型是什么?由T推导出ParamType的类型是什么。
ParamType 是引用或者指针,但不是&&
- 如果expr是引用,在类型推导时忽略它的引用部分
- 然后用expr的类型匹配ParamType
比如模板定义是这样的:
template<typename T>
void f(T& param)
这种可以看作是f的参数一定是一个引用
通过以下几种方式调用:
int x = 2;
const int constx = 2;
const int &constrefx = x;
- f(x) T的类型是int, param的类型是int&
- f(cx) T的类型是const int, param的类型是const int&
- f(constrefx); T的类型是const int, param的类型是const int&
如果模板的参数是已经包含了一个const:
template<typename T>
void f(const T& param)
因为param的参数一定是一个const &,所以在推导T的类型时就不用包含const了:
template<typename T>
void f(const T& param); // 模板参数变成了const &
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // T is int, param's type is const int&
f(cx); // T is int, param's type is const int&
f(rx); // T is int, param's type is const int&
其实就是保证T的类型在推导出param的类型时一定是const &
模板中的const和&,可以看成是T的类型最终推导出的param类型一定是const和&,以此来确定T的类型。
指针也是一样的:
template<typename T>
void f(T* param);
int x=1;
const int *px = &x;
f(&x) // T的类型是int, param的类型是int *
f(px) // T的类型是const int, param的类型是const int*
ParamType是一个Universal Reference
template<typename T>
void f(T&& param);
- 如果调用参数是左值,那么param的类型就是一个左值引用:&,同时如果调用参数有const修饰那么param的类型也会包含const变成const &
- 如果调用参数是一个右值,那么param的类型就是一个右值
template<typename T>
void f(T&& param); // param is now a universal reference
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // x is lvalue, so T is int&, param's type is also int&
f(cx); // cx is lvalue, so T is const int&, param's type is also const int&
f(rx); // rx is lvalue, so T is const int&, param's type is also const int&
f(27); // 27 is rvalue, so T is int, param's type is therefore int&&
ParamType不是引用也不是指针
template<typename T>
void f(T param); // param is now passed by value
- 如果调用参数是一个引用&,则忽略引用&
- 如果调用参数是一个const,也忽略这个const修饰
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // T's and param's types are both int
f(cx); // T's and param's types are again both int
f(rx); // T's and param's types are still both int
这个可以认为是,模板强制参数是无const的值传递,所以无论你传的是const还是引用,都会被忽略,然后推导成一个普通的值类型
通过const指针调用呢:
const char* const ptr = // ptr is const pointer to const object
"Fun with pointers";
f(ptr);
推导步骤:
- 忽略ptr本身的const得到const char*(要明白**指针不能变**和**指针指向的值不能变**),去掉ptr的const后ptr就可以被赋值了。
- T的类型就是const char*,得到param的类型是const char *
数组参数
如果模板参数是值类型:
template<typename T>
void f(T param);
const char name[] = "J. P. Briggs";
f(name)
这种情况数组直接当成指针进行推导,得到T的类型是 const char*,而不是数组
如果模板参数是引用类型:
template<typename T>
void f(T& param);
const char name[] = "J. P. Briggs";
f(name)
这种情况,T的类型则是真正的数组const char[13], param类型则是这个数组的引用:const char (*) [], 通过这种方式式就可以得到取得数组长度的模板函数:
// return size of an array as a compile-time constant. (The
// array parameter has no name, because we care only about
// the number of elements it contains.)
template<typename T, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) noexcept
{
return N;
}
函数指针作为参数
void someFunc(int, double);
template<typename T>
void f1(T param);
template<typename T>
void f2(T& param);
f1(someFunc) // param推导为 ptr to func:void (*) (int, double)
f2(someFunc) // param推导为 ref to func:void (&) (int, double)
就是说对于函数参数:
- 如果模板定义为一个值参数则推导为一个指针
- 如果模板定义为一个引用则推导为引用
auto 类型推导
auto类型推导和模板一样对于:
auto a = ...
可以看成是:
template <typename T>
void f(T a)
来推导a的类型
-
auto type deduction is usually the same as template type deduction, but auto type deduction assumes that a braced initializer represents a std::initial izer_list, and template type deduction doesn’t.
-
auto in a function return type or a lambda parameter implies template type deduction, not auto type deduction.
decltype类型声明
- decltype(varname)的类型推导和变量的类型一模一样,包含const和&都会保留下来
- 对于decltype(expression)一个表达式:
- 如果expression产生一个右值引用&&,则结果也是一个右值引用
- 如果expression产生一个左值,则结果是一个左值引用
- 如果expression产生一个右值,则结果是一个不是引用的普通类型