template<typename T>
void f(ParamType param);
f(expr); // 调用该模板函数
编译期间,编译器会经过expr推导2种类型(T 跟 ParamType); web
类型常常是不一样的,由于ParamType包括了不少修饰符(e.g. const or reference); 数组
如:svg
template<typename T>
void f(const T& param); // ParamType is const T&
int x = 0;
f(x); // call f with an int
那么,
T
被推导成int
类型,而ParamType
被推导成const int&
类型;函数
但T的类型推导,不单单取决于expr
,也取决于ParamType
的型式; spa
【下面的3个案例,讲解了这种特性】: 指针
template<typename T>
void f(ParamType param);
f(expr); // 调用该模板函数
expr
类型是一个引用,则忽略引用部分;ParamType
,模式匹配expr
的类型,以肯定T.template<typename T>
void f(T & param); // param is a reference
int x = 27; // int
int const cx = x; // int const
int const & rx =x; // refernce to x
f(x); // T => int, param's type => int&
f(cx); // T => int const, param's type => const int&
f(rx); // T => int const, param's type => const int&
- 由于cx、ex为const int值,所以,T被推导为const int;
- 当传递const对象给引用参数时,须要保持不可更改性,所以T包含了const部分;
- 类型推导过程当中,会忽略引用类型,因此T不包含&;
template<typename T>
void f(T const ¶m); // param is now a ref-to-const
int x = 27; // int
int const cx = x; // int const
int const & rx =x; // refernce to x
f(x); // T => int, param's type => int const &
f(cx); // T => int, param's type => int const &
f(rx); // T => int, param's type => int const &
- 由于如今假设
ParamType
为ref-to-const,因此const不用做为T的一部分推导;- 照常,对于rx,在推导T的过程当中,忽略其引用类型;
template<typename T>
void f(T* param); // param is now a pointer
int x = 27; // int
int const * px = &x; // int const *(这是一个指向常量的指针,int * const 则是一个指针常量,是一个常量)
f(&x); // T => int, param's type => int *
f(&px); // T => int const, param's type => int const *
- 对于指针,推导T的方式,跟引用是同样的;
expr
是一个左值,T和ParamType都被推导为左值引用;(a.这是仅有的T被推导为引用的状况 b.虽然ParamType的格式是右值引用,但它们的类型还是左值引用)expr
是一个右值,则规则符合Case1;template<typename T>
void f(T && param); // param is now a universal reference
int x = 27; // int
int const cx = x; // int const
int const & rx =x; // refernce to x
f(x); // T => int &, param's type => int &
f(cx); // T => int const &, param's type => int const &
f(rx); // T => int const &, param's type => int const &
f(27); // T => int, param's type => int &&
对于通用引用的类型推导规则,区别于expr是左值仍是右值,而不一样;
对于非通用引用的类型推导,则彻底没有这种特殊的规则;code
template<typename T>
void f(T param); // param is now passed by value
int x = 27; // int
int const cx = x; // int const
int const &rx =x; // refernce to x
f(x); // T => int, param's type => int
f(cx); // T => int, param's type => int
f(rx); // T => int, param's type => int
由于
param
是彻底独立于x、cx、rx的(即它是一份拷贝),这即是为何在推导时,忽略expr的const部分的缘由;
这是ParamType无任何修饰的状况下才成立的,即当为引用或指针时,const仍是没法忽略的;regexp
char* const ptr1 = "abc"; // char * const
const char* const ptr2 = "abc"; // const char * const
f(ptr1); // T => char*, param's type => char * f(ptr2); // T => char const *, param's type => char const *
这里要注意指针常量,跟常量指针,以及常量指针常量的区别;xml
数组类型 于 指针类型,是不用的,虽然有些状况下它们是通用的;
不少状况下,一个数组能衰化成一个指向其第1个元素的指针; 对象
const char name[] = "Hello"; // const char[6]
const char * ptrToName = name; // const char *
虽然能将array转成pointer,但这二者的类型,是不同的;
放到函数参数中:
void myFunc(const char param[]);
void myFunc(const char * param);
myFunc(name);
这两个函数声明是如出一辙的,这是C所遗留到C++上的一个内容,所以,对于
myFunc(name)
的调用,这两个函数均可以,但只能写其中1个,不然会报错;
放到函数模板中:
template<typename T>
void f(T param);
f(name); // T => char const *, param's type => char const *
对于Case3这种类型的模板函数来讲,传入的name数组,由于数组参数声明会被视为指针参数,因此会被推导成 const char *;
解决方法:
template<typename T>
void f(T & param);
f(name); // T => const char[6], param's type => const char(&)[6]
T会被推导成实际的数组类型,类型包括了数组的size;
跟数组同样,函数类型也可以衰化成指针类型;
void someFunc(int,double);
template<typename T>
void f1(T param);
template<typename T>
void f2(T& param);
f1(someFunc); // void(*)(int,double)
f2(somefunc); // void(&)(int,double)