C++源码分析完美转发
- 完美转发作用:
- 可以保持实参数据在函数中的左值或者右值类型。
不使用完美转发的后果
-
#include<iostream> using namespace std; // 容器里面元素的类型 class A { public: A() {} // 带左值引用参数的赋值函数 A& operator=(const A& src) { cout << "operator=&" << endl; return *this; } // 带右值引用参数的赋值函数 A& operator=(A&& src) { cout << "operator=(A&&)" << endl; return *this; } }; // 容器的类型 template<typename _Ty> class Vector { public: // 引用左值的push_back函数 void push_back(const _Ty& val) { mvec[mcur++] = val; } // 引用右值的push_back函数 void push_back(_Ty&& val) { // 这里传递val时,要用move转换成右值引用类型, mvec[mcur++] = val; } private: enum { VEC_SIZE = 10 }; _Ty mvec[VEC_SIZE]; int mcur; }; int main() { Vector<A> vec; A a; vec.push_back(a); // 调用A的左值引用的赋值函数 vec.push_back(A()); // 理应调用A的右值引用参数的赋值函数,却调用了左值引用的赋值函数 return 0; }
-
可见,不使用完美转发,vec.push_back(A()); 理应调用A的右值引用参数的赋值函数,却调用了左值引用的赋值函数
-
原因:
- 因为当传递右值给
push_back(_Ty&& val)
函数时 - 由于
val
是右值引用的形参,它在函数内部被解释为左值引用,因此在函数体内部处理时,它仍然被视为左值。
- 因为当传递右值给
-
所以避免我们传递的右值引用 失去右值的效果,我们引入了 完美转发
引入完美转发
-
源码:
-
template<class _Ty> _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue return (static_cast<_Ty&&>(_Arg)); } template<class _Ty> _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call"); return (static_cast<_Ty&&>(_Arg)); }
-
-
源码实现了两个版本的forward重载函数
-
左值引用版本的
-
调用场景
-
不管传入的是左值右值,形参val都是左值,所以调用的都是forward的左值引用版本
-
template<class _Ty> _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue cout << "remove_reference_t<_Ty>& _Arg" << endl; return (static_cast<_Ty&&>(_Arg)); }
- 如果实参类型是int& + && -> **int&**就保持了实参的左值引用类型
- 如果实参类型是int&& + && -> **int&&**就保持了实参的右值引用类型。
-
-
-
右值引用:
-
只有传入的参数是 右值时,才会调用
-
template<class _Ty> _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call"); return (static_cast<_Ty&&>(_Arg)); }
-
-
-
总结
- 利用完美转发,我们可以可以保持实参数据在函数中的左值或者右值类型,从而达到我们想要的效果