本文最后更新于:1 年前
深入探讨右值引用应用中的完美转发
1.为什么使用完美转发
我们先看以下的案例:
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 29 30 31 32 33
| class CMyClass { public: CMyClass() {}; ~CMyClass() {}; int m_1 = 1; protected: private:
}; void Code_1(int t) { _tprintf(_T("%d"),t); }
void Code_1(CMyClass t) { _tprintf(_T("%d"),t.m_1); }
template<typename T> void Forward_1(T t) { Code_1(t); }
int _tmain() { CMyClass Object; Forward_1(Object); Forward_1(CMyClass()); return 0; }
|
我们的Forward_1函数使用值传递参数,会有以下问题:
所以我们考虑使用引用传递,因为引用传递有以下特性:
所以我们实现以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void Code_2(int& t) { _tprintf(_T("%d"),t); }
void Code_2(CMyClass& t) { _tprintf(_T("%d"),t.m_1); }
template<typename T> void Forward_2(T& t) { Code_2(t); }
int _tmain() { CMyClass Object; Forward_2(Object); Forward_2(CMyClass()); return 0; }
|
但又出现了一个问题:
这就涉及到完美转发,即让一个函数既可以接收左值,又可以接收右值
2.函数重载实现完美转发
我们对引用传递参数的代码进行修改,使其可以初步实现完美转发,主要使用了常函数重载:
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 29
| void Code_2(int& t) { _tprintf(_T("Left")); } void Code_2(const int& t) { _tprintf(_T("Right")) } void Code_2(CMyClass& t) { _tprintf(_T("Left")); } void Code_2(const CMyClass& t) { _tprintf(_T("Right")); } template<typename T> void Forward_1(T& t) { Code_2(t); }
int _tmain() { CMyClass Object; Forward_1(Object); Forward_1(CMyClass()); return 0; }
|
在未出现右值引用这一C++新特性前,常函数重载是实现完美转发的惯用方式
但本文主要介绍使用右值引用实现完美转发的方式,因为常函数重载具有以下缺点:
- 编写大量的重载函数模板 造成代码冗余 因为各个模板都需要重载
- 无法实现移动语义 因为常函数内部无法对成员进行修改 不能move
为了解决以上问题,引入右值引用的方法
3.右值引用实现完美转发
在函数模板中使用右值引用的语法定义参数被解释为可以接收右值,也可以接收左值此时被成为“万能引用”
1 2 3 4 5
| template<typename T> void Forward_2(T&& t) { Code_2(t); }
|
但这是还存在问题:无论传入的形参时左值还是右值,对于模板来说,形参都是可以寻址的,因此都是一个左值
解决方法:
1 2 3 4 5 6 7 8 9 10 11
| template<typename T> void Forward_2(T&& t) { Code_2(forward<T>(t)); }
Forward_3(Object);
Forward_3(CMyClass());
|
右值引用的好处 是可以对成员变量进行一个写操作 相较于常引用的优点 但常引用既可以绑定左值 又可以绑定右值
此时Code_2代码需要重写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void Code_2(int& t) { _tprintf(_T("Left")); } void Code_2(int&& t) { _tprintf(_T("Right")); }
void Code_2(CMyClass& t) { _tprintf(_T("Left")); } void Code_2(CMyClass&& t) { _tprintf(_T("Right")); }
|