在C++11及以上的相关语法中,特别是在模版元编程的范式里,类型推导是了重中之重。
在 《Effective Modern C++ 》 中第一章主要就是讲各种类型推导。
当然了,谈到类型推导,我们不得不先搞懂类型推导中的模式匹配,这是基础,本博客主要就是论述类型推导中的模式匹配。
随后,本文将直接开始推倒 《Effective Modern C++》 中三大情况的各个例子,展现推导的具体思路
问题定义
函数模版大致形如:
cpp
template<typename T>
void f(ParamType param);
//一次调用则形如:
f(expr);
在编译期,编译器会通过 expr
推导两个型别,一个是 T
的型别,一个是 ParamType
的型别:
cpp
template<typename T>
void f(const T& param); //ParamType 是 const T&
//调用语句如下:
int x = 0;
f(x) //以一个 int 调用 f
我们可以依靠直觉就能得出正确答案: T
的型别是 int
,ParamType
的型别是 const int&
模式匹配的相关概念
在刚才,其实我们下意识得就使用了C++语法中的模式匹配:
首先需要明确的是,整个模式匹配行为就是:
对 expr
对型别和 ParamType
的型别执行模式匹配,来决定 T 的型别。
对于以下案例:
cpp
template<typename T>
void f(const T& param);
int a = 27;
int* x = &a;
f(x);
匹配过程
根据模版类型推导的规则:
- 明确传递的参数
x
的类型是int*
。 - 函数参数的类型是
const T&
为了匹配 int*
和 const T&
,编译器会去掉参数类型中的 const
和引用符号 (为什么能够去掉呢?在文末我会进行解答),然后将剩余的部分与 T
进行匹配:
- 参数的类型是
int*
,而函数参数的类型是const T&
- 去掉
const
和引用后,剩下的类型是int*
。
因此 T
被推导为 int*
;
最后我们把 T
套入 ParamType
,可知 ParamType
的类型是 const int*&
(同 int* const &),即指针的引用,并且还有一个底层 const
。
总结
一起来解答之前提出的相关问题吧:
- 「去掉参数类型中的
const
和引用符号」为什么能去掉他俩呢?请看下面对引用折叠的相关描述,其实为什么要去掉 const 也是一样的逻辑,如果我们的 ParamType 中不带 const 的话当然不用去掉,但是一旦 ParamType 中带有 const 就应该去掉实参自带的 const 。
- 「如果 expr 具有引用类型,先将引用部分忽略」为什么引用部分可以忽略呢?
首先我们要明确,引用本身在C++中就是一个类型修饰符,用于表示一个对象的别名(尽管其底层实现是指针),但是引用的引用是没有意义的。所以编译器在推导类型时会忽略引用部分。
主要就是在C++标准中规定,引用的引用会折叠成一个引用,具体的规则如下:
T& &
和T& &&
折叠为T&
。我们忽略引用部分就是因为这一条T&& &
折叠为T&
T&& &&
折叠为T&&