关于Modern C++中Universal Reference的一些理解和谬误

Universal Reference只是一种技巧,并不是一种C++中内建的语法糖。实际上所谓Universal Reference本身只是模板推导、引用折叠以及类型退化共同构成的一种编程范式。

通用引用是否就是形如T&&的引用?

不是。通用引用成立的非常重要的条件之一就是模板类型推导。T必须是一个独立的、构成推导条件的模板类型参数。比如:

通用引用正确示例

template<typename T>
void uniref(T &&dat) {
// TODO
}

通用引用错误示例

template<typename T>
struct test {
void uniref(T &&dat) {
// TODO
}
};

通用引用的推导规则是怎样的?

首先需要介绍的概念是引用折叠。引用折叠本身也是存在于模板类型推导的过程中

引用折叠仅存在于使用通用引用承载左值时。大家都知道左值引用的形式为T&,若套用在通用引用的形式中,就会变成(T&)&&,这时外侧的&&会被自动忽略掉。这个行为被称为引用折叠。

其次是类型退化。退化的具体规则比较复杂,我们在这里仅介绍在通用引用中的类型退化。事实上之所以推荐在传递参数时使用通用引用,就是因为通用引用中发生的类型退化几乎是所有传参方式中最小的。

当通用引用承载右值时,注意这里是右值而不是右值引用,T&&会退化为平凡参数,或者说是退化为传值(Pass by value)。

最后是最简单的类型匹配。对于本身就是右值引用的参数,这时发生的是最简单的类型匹配。如传入int&&,通用引用的T&&将构成对这个类型的全匹配,因此不会发生任何多余的修饰。

也就是说,通用引用的推导规则可总结为下表:

参数左值左值引用右值右值引用
修饰折叠折叠退化全匹配
T&&T&T&TT&&

常见谬误

通用引用能保留参数的原有类型

并不能,具体可见上表中的推导规则。通用引用常用于实现完美转发,而完美转发依赖std::forward对类型进行还原。

若不进行还原,对于传入的右值很容易被判定为左值。这是传值带来的附加效果。事实上这里在函数返回之前的确是一个左值。

右值引用就是通用引用

长得像罢了

更多问题,可以在评论中回复。

发表回复