序
c++11 最终要的是拓展了类型推断(type deduction)使用的地方,比如auto
,decltype
等。所以要详细的理解类型推断,以正确的使用。
类型推断时,在如下代码中,我们要确定T
和ParamType
的类型。
template<typename T>
void f(ParamType param) ;
f(expr);
依据ParamType
的不同,有三种情况需要讨论
ParamType
是引用或者指针,但不是universal references
ParamType
是universal references
ParamType
既不是引用也不是指针
ParamType
是引用或者指针,但不是universal references
这种情况下,T的类型推断遵循两个规则:
- 如果
expr
是引用类型,忽略引用部分 expr
和ParamType
进行,模式匹配,差异的地方决定了T
的类型
下面是例子:
// case 1
templaye<typename T>
void f(T& param);
int x = 27; // x 是 int
const int cx = x; // cx 是 const int
const int& rx = x; // rx 是 const int 的引用
f(x); // T is int, param's type is int&
f(cx); // T is const int, param's type is const int&
f(rx); // T is const int, param's type is const int&
// case 2
templaye<typename T>
void f(const T& param);
int x = 27; // x 是 int
const int cx = x; // cx 是 const int
const int& rx = x; // rx 是 const int 的引用
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&
// case 3
templaye<typename T>
void f(T* param);
int x = 27; // x 是 int
const int *px = &x; // px 是一个指向 const int 的指针
f(&x); // T is int, param's type is int*
f(px); // T is const int, param's type is const int*
ParamType
是universal references
这种情况下,T
和ParamType
的类型推断遵循两个规则:
- 如果
expr
是左值,T
和ParamType
都会被推断为左值引用。这是在类型推断中,唯一出现引用的地方。 - 如果
expr
是右值,则回到第一种情况。
下面是例子:
template<typename T>
void f(T&& param);
int x = 27;
const int cx = x;
cont int & rx = x;
f(x); // x 是左值,T 是 int&,param 是 int&
f(cx); // cx 是左值,T 是 const int&,param 是 const int&
f(rx); // rx 是左值,T 是 const int&,param 是 const int&
f(27); // 27 是右值,T 是 int,param 是 int&&
ParamType
既不是引用也不是指针
在这种情况下,参数使用值传递
此时,使用如下规则:
- 如果
expr
是引用,忽略引用部分 - 如果,在忽略了
expr
引用部分后,expr
是const
,volatile
,同样被忽略。
下面是例子:
template<typename T>
void f(T param); // param 是值传递
int x = 27;
const int cx = x;
cont int & rx = x;
f(x); // T 是 int,param 是 int
f(cx); // T 是 int,param 是 int
f(rx); // T 是 int,param 是 int
考虑一种情况,expr
是一个指向常量对象的常量指针。
template<typname T>
void f(T param);
const char* const ptr = "fun with pointers";
f(ptr); // T 是 const char *, param 是 const char *
特殊的情况:数组参数
需要知晓的是,数组并不同于指针。
const char name[] = "J. P. Briggs"; // name 的类型是 const char[13]
const char * ptrToName = name; // ptrToName 的类型是 const char *
template<typename T>
void f(T param);
f(name); // T 是 const char *,param 是 const char *
虽然,函数不能接受数组参数,但是能够接受数组引用作为参数,所以
template<typename T>
void f(T& param);
f(name); // T 是 const char[13],param 是 const char(&)[13]
有意思的是,正因为数组引用这种看起来奇怪的推断,导致我们能够在模板中推断出数组长度
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); // T 是 void(*)(int, double), param 是 void(*)(int, double)
f2(someFunc); // T 是 (void)(int, double), param 是 void(&)(int, double)
虽然,在使用上,函数的引用和函数的指针,没有什么显著的区别,但是还是要注意到类型推断上的不同。
牢记
- 类型推断时,引用参数被当做非引用参数对待,也就是说,他们的引用被忽略了。
- 当推断类型是
universal reference
参数时,左值被特殊对待了。 - 当推断类型是值传递时,
const
和volatile
都被忽略了。 - 在模板类型推断时,数组和函数被隐式推断成了指针,除非
param
是个引用。