1. std::forawrd
std::forward<T>(arg)
可以实现完美转发,即如果 arg
是一个右值引用,则转发之后结果仍是右值引用;反之,如果 arg
是一个左值引用,则转发之后结果仍是左值引用.
#include <iostream>
struct BigObject
{
char data[1 << 10];
};
void g(BigObject& o)
{
std::cout << "lvalue reference\n";
}
void g(BigObject&& o)
{
std::cout << "rvalue reference\n";
}
template <typename T>
void f(T&& arg)
{
g(std::forward<T>(arg));
}
int main()
{
BigObject o;
f(o);
f(BigObject());
}
lvalue reference
rvalue reference
2. 为什么需要完美转发
- 在函数模板编程中,常有一种场景是使用模板参数去调用另一个函数(如,上例中
f
去调用 g
),这时候如果只提供值传递版本会显得效率太低。 - 函数的参数一般会尽可能地设为引用类型,以避免对象拷贝带来的高昂开销.
- 为了使一个函数既可以接受左值,又可以接受右值,C++11 之前的解决方案是将参数类型设为
const Type&
. 但这并不是很方便,如限制了参数是常量. - 如果函数
g
既提供了左值引用版本和右值引用版本,则最好的情况是函数 f
可以根据参数类型去调用相应版本的 g
. 而完美转发正可以满足此要求.
3. 引用折叠规则
- 右值引用和右值引用叠加将得到右值引用;
- 右值引用和左值引用叠加将得到左值引用;
- 左值引用和左值引用叠加将得到左值引用.
template <typename T>
using TR = T&;
TR<int> v;
TR<int>& v;
TR<int>&& v;
template <typename T>
using TRR = T&&;
TRR<int> v;
TRR<int>& v;
TRR<int>&& v;
4. 完美转发的原理
template< class T >
T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
template< class T >
T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;
std::remove_reference<T>
的作用就是去掉 T
中的引用,它是通过模板特化来实现:
template< class T > struct remove_reference {typedef T type;};
template< class T > struct remove_reference<T&> {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};
根据上述引用折叠规则,如果 T
是 int&
,则 T&&
即为 int&
;反之,如果 T
是 int&&
,则 T&&
为 int&&
.
5. forwarding reference
上例函数 f
中的 T&&
实际上被称为 forwarding reference. 它是一种特殊类型的引用,它保留了函数参数的值类别(category),使得可以通过 std::forward
来转发它.
forwarding reference 包括以下两种:
-
在函数模板中,没有被 const
或 volatile
修饰的、声明为右值引用的类型形参:
template<class T>
int f(T&& x) {
return g(std::forward<T>(x));
}
template<class T>
int g(const T&& x);
-
auto&&
,但如果它跟着一个花括号括起的初始值列表,则它不是 forwarding reference:
auto&& vec = foo();
g(std::forward<decltype(vec)>(vec));
auto&& z = {1, 2, 3};
for (auto&& x: f()) {
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)