0%

聊聊C++中的完美转发

之前我已经向你介绍了C++中的std::move,它的作用是无论你传给它的是左值还是右值,通过std::move之后都变成了右值。而今天我们要介绍的std::forward则与之不同,它的作用是什么呢?

forward的作用

std::forward被称为完美转发,它的作用是保持原来的属性不变。啥意思呢?通俗的讲就是,如果原来的值是左值,经std::forward处理后该值还是左值;如果原来的值是右值,经std::forward处理后它还是右值。

看看下面的例子,你应该就清楚上面这句话的含义了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<iostream>

template<typename T>
void print(T & t){
std::cout << "lvalue" << std::endl;
}

template<typename T>
void print(T && t){
std::cout << "rvalue" << std::endl;
}

template<typename T>
void testForward(T && v){
print(v);
print(std::forward<T>(v));
print(std::move(v));
}

int main(int argc, char * argv[])
{
testForward(1);

std::cout << "======================" << std::endl;

int x = 1;
testFoward(x);
}

在上面的代码中,定义了两个模板函数,一个接收左值,另一个接收右值。在testForward函数中向模板函数print传入不同的参数,这样我们就可以观察出forward与move的区别了。

上面代码执行结果如下:

1
2
3
4
5
6
7
lvalue
rvalue
rvalue
=========================
lvalue
lvalue
rvalue

从上面第一组的结果我们可以看到,传入的1虽然是右值,但经过函数传参之后它变成了左值(在内存中分配了空间);而第二行由于使用了std::forward函数,所以不会改变它的右值属性,因此会调用参数为右值引用的print模板函数;第三行,因为std::move会将传入的参数强制转成右值,所以结果一定是右值。

再来看看第二组结果。因为x变量是左值,所以第一行一定是左值;第二行使用forward处理,它依然会让其保持左值,所以第二也是左值;最后一行使用move函数,因此一定是右值。

通过上面的例子我想你应该已经清楚forward的作用是什么了吧?

forward实现原理

要分析forward实现原理,我们首先来看一下forward代码实现。由于我们之前已经有了分析std::move的基础,所以再来看forward代码应该不会太困难。

1
2
3
4
5
6
7
8
9
10
11
template <typename T>
T&& forward(typename std::remove_reference<T>::type& param)
{
return static_cast<T&&>(param);
}

template <typename T>
T&& forward(typename std::remove_reference<T>::type&& param)
{
return static_cast<T&&>(param);
}

forward实现了两个模板函数,一个接收左值,另一个接收右值。在上面有代码中:

1
typename std::remove_reference<T>::type

的含义我们在分析std::move时已经向你做了说细的说明,其作用就是获得去掉引用后的参数类型。所以在上面的两上模板函数中,第一个是左值引用模板函数,第二个是右值引用模板函数。

紧接着forward模板函数对传入的参数进行强制转换,转换的目标类型符合引用折叠规则,因此左值参数最终转换后仍为左值,右值参数最终转成右值。

小结

本文我们首先通一个小例子向你介绍了std::forward的作用为“完美转发”,也就是不改变原值的属性。接着我带你分析了std::forward的原码实现,如果你看过我之前对std::move的分析文章,相信你在阅读std:forward的代码实现时不会遇到什么困难。

参考

C++高阶知识:深入分析移动构造函数及其原理
细说智能指针
聊聊C++中的类型转换
重学C/C++中的const

欢迎关注我的其它发布渠道