引用折叠规则
引用折叠是 C++ 中处理引用类型推导的规则。具体规则如下:
T& &
变为T&
T& &&
变为T&
T&& &
变为T&
T&& &&
变为T&&
1.万能引用:
我们知道左值和右值引用,其中右值引用应用于实现移动构造和复制构造,比如:
MyClass(MyClass&& other),MyClass& operator=(MyClass&& other)
万能引用就是既可以传左值,又可以传右值,万能引用的前提:必须是函数模板,也就是说,在右值引用的前提下加上函数模板,就升级成了万能引用。
-
当传递左值(如
x
)时:T
会被推导为int&
,因此T&&
实际上变为int& &&
,这是一个右值引用类型。- 结果是
arg
变为int&
(左值引用),所以typeid(arg).name()
打印的结果是i
,表示int&
。
-
当传递右值(如
20
)时:T
会被推导为int
,因此T&&
实际上变为int&&
,这是一个右值引用类型。- 结果是
arg
变为int
(右值),所以typeid(arg).name()
打印的结果是i
,表示int
。
如果要区分两个i,可以用is_lvalue_reference<T>::value,如果是左值,会返回true
2.完美转发
std::forward
是实现完美转发的关键工具。它根据传递给它的模板参数的值类别来决定如何转发参数。- 如果
T
是左值引用,则std::forward<T>(arg)
将arg
转发为左值引用;如果T
是右值引用,则std::forward<T>(arg)
将arg
转发为右值引用。
为什么要用完美转发:
使用完美转发vs不使用完美转发
(1)使用:
-
myForward(x)
调用:T
被推导为int&
,因此std::forward<T>(arg)
将arg
以左值的形式传递给process
。process
函数中的T&&
被折叠为int&
,保留了x
的左值特性。
-
myForward(20)
调用:T
被推导为int
,因此std::forward<T>(arg)
将arg
以右值(T&&)的形式传递给process
。process
函数中的T&&
被折叠为int&&
,保留了20
的右值特性。
(2)不使用
-
myForward(x)
调用:T
被推导为int&
,因此T&&
变为int& &&
。int& &&
实际上是int&
,因为左值引用不能绑定到右值引用上。- 传递到
process
的arg
类型是int&
,即使arg
是左值,process
函数中的arg
类型也是int&
,这意味着在process
中处理的是x
的左值特性。
-
myForward(20)
调用:T
被推导为int
,因此T&&
变为int&&
。- 传递到
process
的arg
类型是int
,即使arg
是右值,process
函数中的arg
类型也是int
,这丢失了右值的特性,被当做普通的值进行处理。
不使用完美转发的影响:
-
丢失右值特性:
- 当
T
是左值引用时,T&&
的折叠结果是左值引用(int&
)。如果传递的是右值(如20
),在process
函数中,arg
的类型仍然是int
,丢失了右值特性。这样,可能会导致右值被当作左值处理,从而可能影响性能(如不必要的拷贝)。
- 当
-
可能不必要的拷贝或移动:
- 如果在
process
函数中,你希望进行移动操作(例如使用std::move
),但由于没有使用std::forward
,右值传递可能被当作左值处理,导致拷贝操作而非移动操作,这会影响效率。
- 如果在