文章参考:
1. move
作用:主要作用有两点:
- 实现用左值来初始化右值引用。
- 进行对象间的资源转移。(避免使用拷贝,从而提高效率)
特点:
- 可以将左值转化为右值。
- 该函数和移动构造函数一样,都有移动语义,将对象的状态或者所有权从一个对象转移到另一个对象,且进行内存拷贝。
原型:从实现上看,std::move()
基类等同于一个类型转移static_cast<T&&>(lvalue)
template <class _Ty>
_NODISCARD constexr remove_reference_t<_Ty>&& move(_Ty&& _Arg) _NOEXCEPT{
return static_cast<remove_reference_t<_Ty>&&> (_Arg));
}
Eg:
-
用左值来初始化右值引用
#include <iostream> class Test { public: Test(){} }; int main(void){ Test t; Test& lt = t; Test&& rt = std::move(t); return 0; }
- t是左值,但通过
std::move()
,可以用左值对右值引用进行初始化。
- t是左值,但通过
-
实现对象间的资源转移,无需复制:
#include <iostream> #include <list> using namespace std; int main(void){ list<int> l1 = {1, 2, 3, 4, 5}; list<int> l2 = move(l1); cout << "l1:"; for(auto t: l1){ cout << t << " "; } cout<<endl; cout << "l2:"; for(auto t : l2){ cout << t << " "; } cout << endl; return 0; }
-
输出:
l1: l2:1 2 3 4 5
-
分析:因为使用了
std::move()
,列表l1的资源被转移给了列表l2,因此列表l1没有资源了。
-
2. forward
引入:
右值引用是独立于值的,当一个右值引用作为函数的形参时,如果在该函数内部调用该形参,将其转发给其余函数时,该形参就变成了一个左值。如果我们想要它依旧是一个右值,那么就要使用C++11提供的std::forward()
函数,也就是所谓的完美转发
。
函数原型:
template <class T>
T&& forward (typename remove_reference<T>::type& t) noexcept;
template <class T>
T&& forward (typename remove_reference<T>::type&& t) noexcept;
-
精简后:
std::forward<T>(t)
效果:
- 如果传入的T是左值引用类型,那么t将会被转换为T类型的左值。
- 当T不是左值引用类型时,t将会被转换为T类型的右值。
EG:
-
代码:
#include <iostream> using namespace std; template<typename T> void printValue(T& t) { cout << "l-value: " << t << endl; } template<typename T> void printValue(T&& t) { cout << "r-value: " << t << endl; } template<typename T> void testForward(T && v) { printValue(v); printValue(move(v)); printValue(forward<T>(v)); cout << endl; } int main() { testForward(520); int num = 1314; testForward(num); testForward(forward<int>(num)); testForward(forward<int&>(num)); testForward(forward<int&&>(num)); return 0; }
-
输出:
l-value: 520 r-value: 520 r-value: 520 l-value: 1314 r-value: 1314 l-value: 1314 l-value: 1314 r-value: 1314 r-value: 1314 l-value: 1314 r-value: 1314 l-value: 1314 l-value: 1314 r-value: 1314 r-value: 1314
-
分析:
testForward(520)
:函数形参T&&
为未定义引用类型,实参为右值,初始化后被推导为一个右值引用。printValue(v)
:已命名的右值v,编译器会视作左值处理,因此实参是左值
。printValue(move(v))
:已命名的右值被编译器视作左值处理,随后又通过move
转换为右值,因此实参是右值
。printValue(forward<T>(v))
:已命名的右值被编译器视作左值处理,而此处的模板参数T
为右值引用,因此参数被完美转发成一个右值,因此实参是右值
。
testForward(num)
:函数形参T&&
为未定义引用类型,实参为左值,初始化后被推导为一个左值引用。printValue(v)
:实参是左值
。printValue(move(v))
:左值通过move
转换为右值,因此实参是右值
。printValue(forward<T>(v))
:forward的模板参数为左值引用,最终得到一个左值引用,实参为左值
。
testForward(forward<int>(num))
:forward的模板参数不是左值引用,因此num被完美转发为右值
,函数形参T&&
为未定义引用类型,最终实参为右值
。printValue(v)
:已命名的右值v,编译器会视作左值处理,因此实参是左值
。printValue(move(v))
:已命名的右值被编译器视作左值处理,随后又通过move
转换为右值,因此实参是右值
。printValue(forward<T>(v))
:已命名的右值被编译器视作左值处理,而此处的模板参数T
为右值引用,因此参数被完美转发成一个右值,因此实参是右值
。
testForward(forward<int&>(num))
:forward的模板参数int&
是左值引用,因此num被完美转发为左值
,函数形参T&&
为未定义引用类型,最终实参为左值
。printValue(v)
:实参是左值
。printValue(move(v))
:左值通过move
转换为右值,因此实参是右值
。printValue(forward<T>(v))
:forward的模板参数为左值引用,最终得到一个左值引用,实参为左值
。
testForward(forward<int&&>(num))
:forward的模板参数int&&
不是左值引用,因此num被完美转发为右值
,函数形参T&&
为未定义引用类型,最终实参为右
。printValue(v)
:已命名的右值v,编译器会视作左值处理,因此实参是左值
。printValue(move(v))
:已命名的右值被编译器视作左值处理,随后又通过move
转换为右值,因此实参是右值
。printValue(forward<T>(v))
:已命名的右值被编译器视作左值处理,而此处的模板参数T
为右值引用,因此参数被完美转发成一个右值,因此实参是右值
。