C++类型推断
对于静态语言来说,你一般要明确告诉编译器变量或者表达式的类型。但是庆幸地是,现在C++已经引入了自动类型推断:编译器可以自动推断出类型。在C++11
之前,类型推断只是用在模板上。而C++11
通过引入两个关键字auto
和decltype
扩展了类型推断的应用。C++14
更进一步扩展了auto
和decltype
的应用范围。明显地,类型推断可以减少很多无必要的工作。但是高兴之余,你仍然有可能会犯一些错误,如果你不能深入理解类型推断背后的规则与机理。因此,我们分别从模板类型推断、auto
和decltype
的使用三个方面深入讲解类型推断。
模板类型推断
模板类型推断在C++98
中就已经引入了,它也是理解auto
与decltype
的基石。下面是一个函数模板的通用例子:
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); // 使用int类型调用函数
此时类型推断结果是:T
的类型是int
,但是ParamType
的类型却是const int&
。所以,两个类型并不相同。还有,你可能很自然地认为T
的类型与表达式expr
是一样的,比如上面的例子:两者是一样的。但是实际上这也是误区:T
的类型不仅取决于expr
,也与ParamType
紧紧相关。这存在三种不同的情形:
情形1:ParamType是指针或者引用类型
最简单的情况ParamType
是指针或者引用类型,但不是通用引用类型(&&)。此时,类型推断要点是:
- 如果
expr
是引用类型,那就忽略引用部分; - 通过相减
expr
与ParamType
的类型来决定T
的类型。
比如,下面是引用类型的例子:
template <typename T>
void f(T& param); // param是引用类型
int x = 27; // x是int类型
const int cx = x; // cx是const int类型
const int& rx = x; // rx是const int&类型
f(x); // 此时T为int,而param是int&
f(cx); // 此时T为const int,而param是const int&
f(rx); // 此时T为const int,而param是const int&
其中可以看到,const对象传递给接收T&
参数的函数模板时,const属性是能够被T
所捕获的,即const称为T
的一部分。同时,引用类型对象的引用属性是可以忽略的,并没有被T
所捕获。上面处理的其实是左值引用,对于右值引用,规则是相同的,但是右值引用的通配符T&&
还有另外的含义,会在后面讲。
如果param
是常量引用类型,推断也是相似的,尽管有些区别:
template <typename T>
void f(const T& param); // param是常量引用类型
int x = 27; // x是int类型
const int cx = x; // cx是const int类型
const int& rx = x; // rx是const int&类型
f(x); // 此时T为int,而param是const int&
f(cx); // 此时T为int,而param是const int&
f(rx); // 此时T为int,而param是const int&