C++ std::move()和完美转发
std::move()、std::forward<T>、模板類型推斷分析
?
引用折疊原則和完美轉(zhuǎn)發(fā)是有聯(lián)系的,可以說后者是基于前者的某些特性實現(xiàn)的,具體來看一下。
要理解完美轉(zhuǎn)發(fā),需要了解兩個知識點:
引用折疊原則(Reference collapsing rules)。
右值函數(shù)模版參數(shù)類型推導(dǎo)(Template argument deduction)
我們先來分析一下為什么需要使用到move呢?
C++11多出來一個move語義,意圖是解決臨時對象重復(fù)拷貝和釋放引發(fā)的資源浪費,move與右值引用進(jìn)行搭配可以完美的解決這個問題。
比如在進(jìn)行vector中的insert函數(shù)的時候,如果模板類型是string,通過move(str1)我們就可以只將string中的指針進(jìn)行交換,即可實現(xiàn)插入到vector容器中的操作。而不用再進(jìn)行深拷貝的動作。
需要注意的是,如果使用move進(jìn)行左值變右值之后,該左值不能再利用,因為它里面的信息可能已經(jīng)被更改,對應(yīng)的string就是,指向字符串的指針被打斷了。
下面這張圖是模板類型推斷原則:
?
這是函數(shù)模板參數(shù)類型推導(dǎo)中一種比較特殊的情況,這種情況會把模板參數(shù)作為右值引用使用,例如:
- ?
其中T為模板類型,T&&為參數(shù)類型。這種情況會產(chǎn)生兩種結(jié)果:
1. 當(dāng)傳給foo函數(shù)的參數(shù)是一個左值引用時,例如:
int i = 29; foo(i);//i為左值引用- ?
此時,T的類型為int的左值引用:int&,參數(shù)類型為int & &&,(既T&&),結(jié)合上面的引用折疊規(guī)則,最終參數(shù)的類型為int的左值引用:int&。
2. 當(dāng)傳給foo函數(shù)的參數(shù)是一個右值引用時,例如:
- ?
此時,T的類型為int,參數(shù)類型為int&&,(既T&&)。
那么,為什么需要forward呢?
我們先來看下以下例子:
- ?
該函數(shù)第進(jìn)入show之后會調(diào)用show1的調(diào)用,如果此時沒有用forward進(jìn)行轉(zhuǎn)發(fā)的話,會調(diào)用到
左值引用版本,而不會調(diào)用到右值引用的版本。所有forward就是進(jìn)行完美轉(zhuǎn)發(fā)的作用,當(dāng)傳入的是左值引用
的時候,會調(diào)用到左值引用的版本,當(dāng)調(diào)用到右值引用的時候,函數(shù)里面也會調(diào)用到右值引用的版本 那么他是怎么實現(xiàn)的呢?
通過以上代碼的分析,我們可以發(fā)現(xiàn),forward會進(jìn)行類型的轉(zhuǎn)發(fā)。具體實現(xiàn)原理就是上面所說的引用折疊原則。
下面是VS2015下的實現(xiàn)源碼。
// 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));}我們就上面那個例子來進(jìn)行分析:
1、傳入的是左值的時候。
此時forward會調(diào)用下面這個版本的函數(shù)。
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 &,完成了左值的轉(zhuǎn)發(fā)
2、傳入的是右值的時候。
下面是類型推斷調(diào)用的時候生成的函數(shù)。
void show(int && a) {//此時T為 int類型。a為 int&& 類型show1(forward<int>(a)); }- ?
相應(yīng)地就會調(diào)用到下面這個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會返回右值引用,則實現(xiàn)了右值引用的完美轉(zhuǎn)發(fā)。
總結(jié)
1、move函數(shù)是將一個左值轉(zhuǎn)為右值引用,轉(zhuǎn)換之后,改左值就不能再進(jìn)行使用了。
2、forward函數(shù)是進(jìn)行類型的轉(zhuǎn)發(fā),可以將左值引用繼續(xù)轉(zhuǎn)發(fā)為左值,右值引用繼續(xù)轉(zhuǎn)發(fā)為右值引用。
總結(jié)
以上是生活随笔為你收集整理的C++ std::move()和完美转发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《算法第四版》环境搭建
- 下一篇: c++ 线程间通信方式