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& | T | T&& |
常见谬误
通用引用能保留参数的原有类型
并不能,具体可见上表中的推导规则。通用引用常用于实现完美转发,而完美转发依赖std::forward
对类型进行还原。
若不进行还原,对于传入的右值很容易被判定为左值。这是传值带来的附加效果。事实上这里在函数返回之前的确是一个左值。
右值引用就是通用引用
长得像罢了
更多问题,可以在评论中回复。