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 rvaluereturn (static_cast<_Ty&&>(_Arg));}template<class _Ty>_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept{ // forward an rvalue as an rvaluestatic_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 rvaluecout << "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 rvaluestatic_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");return (static_cast<_Ty&&>(_Arg));}
-
-
-
-
总结
- 利用完美转发,我们可以可以保持实参数据在函数中的左值或者右值类型,从而达到我们想要的效果