C++ std::move()和完美转发
std::move()、std::forward<T>、模板類型推斷分析
?
引用折疊原則和完美轉發是有聯系的,可以說后者是基于前者的某些特性實現的,具體來看一下。
要理解完美轉發,需要了解兩個知識點:
引用折疊原則(Reference collapsing rules)。
右值函數模版參數類型推導(Template argument deduction)
我們先來分析一下為什么需要使用到move呢?
C++11多出來一個move語義,意圖是解決臨時對象重復拷貝和釋放引發的資源浪費,move與右值引用進行搭配可以完美的解決這個問題。
比如在進行vector中的insert函數的時候,如果模板類型是string,通過move(str1)我們就可以只將string中的指針進行交換,即可實現插入到vector容器中的操作。而不用再進行深拷貝的動作。
需要注意的是,如果使用move進行左值變右值之后,該左值不能再利用,因為它里面的信息可能已經被更改,對應的string就是,指向字符串的指針被打斷了。
下面這張圖是模板類型推斷原則:
?
這是函數模板參數類型推導中一種比較特殊的情況,這種情況會把模板參數作為右值引用使用,例如:
- ?
其中T為模板類型,T&&為參數類型。這種情況會產生兩種結果:
1. 當傳給foo函數的參數是一個左值引用時,例如:
int i = 29; foo(i);//i為左值引用- ?
此時,T的類型為int的左值引用:int&,參數類型為int & &&,(既T&&),結合上面的引用折疊規則,最終參數的類型為int的左值引用:int&。
2. 當傳給foo函數的參數是一個右值引用時,例如:
- ?
此時,T的類型為int,參數類型為int&&,(既T&&)。
那么,為什么需要forward呢?
我們先來看下以下例子:
- ?
該函數第進入show之后會調用show1的調用,如果此時沒有用forward進行轉發的話,會調用到
左值引用版本,而不會調用到右值引用的版本。所有forward就是進行完美轉發的作用,當傳入的是左值引用
的時候,會調用到左值引用的版本,當調用到右值引用的時候,函數里面也會調用到右值引用的版本 那么他是怎么實現的呢?
通過以上代碼的分析,我們可以發現,forward會進行類型的轉發。具體實現原理就是上面所說的引用折疊原則。
下面是VS2015下的實現源碼。
// TEMPLATE FUNCTION forward template<class _Ty> inlineconstexpr _Ty&& forward(typename remove_reference<_Ty>::type& _Arg) _NOEXCEPT{ // forward an lvalue as either an lvalue or an rvaluereturn (static_cast<_Ty&&>(_Arg));}template<class _Ty> inlineconstexpr _Ty&& forward(typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT{ // forward an rvalue as an rvaluestatic_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");return (static_cast<_Ty&&>(_Arg));}我們就上面那個例子來進行分析:
1、傳入的是左值的時候。
此時forward會調用下面這個版本的函數。
constexpr int& && forward(typename remove_reference<int&>::type& _Arg) _NOEXCEPT{ // forward an lvalue as either an lvalue or an rvaluereturn (static_cast<int& &&>(_Arg));}那么,此時返回的就是int& &&類型,通過引用折疊原則即為 int &,完成了左值的轉發
2、傳入的是右值的時候。
下面是類型推斷調用的時候生成的函數。
void show(int && a) {//此時T為 int類型。a為 int&& 類型show1(forward<int>(a)); }- ?
相應地就會調用到下面這個forward版本:
constexpr int && forward(typename remove_reference<int>::type&& _Arg) _NOEXCEPT { // forward an rvalue as an rvalue static_assert(!is_lvalue_reference<int>::value, "bad forward call"); return (static_cast<int &&>(_Arg)); }我們可以看出上面的這個forward會返回右值引用,則實現了右值引用的完美轉發。
總結
1、move函數是將一個左值轉為右值引用,轉換之后,改左值就不能再進行使用了。
2、forward函數是進行類型的轉發,可以將左值引用繼續轉發為左值,右值引用繼續轉發為右值引用。
總結
以上是生活随笔為你收集整理的C++ std::move()和完美转发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《算法第四版》环境搭建
- 下一篇: 有些事不用听别人的