引用折叠¶
引用折叠会出现在四种语境中:
- 模板实例化
auto类型推断decltype类型推断typedef或using别名声明
引用的引用是非法的:
int a = 1;
int& & b = a; //@ 错误
当左值传给接受转发引用的模板时,模板参数就会推断为引用的引用:
template<typename T>
void f(T&&);
int i = 1;
f(i); //@ T为int&,T& &&变成了引用的引用,于是需要引用折叠的机制
为了使实例化成功,编译器生成引用的引用时,将使用引用折叠的机制,规则如下:
& + & → &
& + && → &
&& + & → &
&& + && → &&
引用折叠是 std::forward 的支持基础:
//@ 不完整的实现
template<typename T>
T&& forward(remove_reference_t<T>& x)
{
return static_cast<T&&>(x); //@ 如果传递左值A,T推断为A&,此时需要引用折叠
}
//@ 传递左值A时相当于
A&&& forward(remove_reference_t<A&>& x)
{
return static_cast<A&&&>(x);
}
//@ 简化后
A& forward(A& x)
{
return static_cast<A&>(x);
}
//@ 传递右值A相当于
A&& forward(remove_reference_t<A>& x)
{
return static_cast<A&&>(x);
}
//@ 简化后
A&& forward(A& x)
{
return static_cast<A&&>(x);
}
auto&& 与使用转发引用的模板原理一样:
int a = 1;
auto&& b = a; //@ a是左值,auto被推断为int&,int& &&折叠为int&
decltype 同理,如果推断中出现了引用的引用,就会发生引用折叠。
如果在 typedef 的创建或求值中出现了引用的引用,就会发生引用折叠。
template<typename T>
struct A {
using RvalueRef = T&&; //@ typedef T&& RvalueRef;
};
int a = 1;
A<int&>::RvalueRef b = a; //@ int& &&折叠为int&,int& b = a
并且 top-level cv 限定符会被丢弃:
using A = const int&; //@ low-level
using B = int&&; //@ low-level
static_assert(std::is_same_v<volatile A&&, const int&>);
static_assert(std::is_same_v<const B&&, int&&>);