天天看點

C++ std::move()和std::forwardstd::move()std:forward

std::move()

隻是做了個類型轉換,它隻保證轉換後的結果是右值。

且并不是轉換成了右值,就一定能保證發生移動而不是拷貝,如:

class Annotation {
public:
 explicit Annotation(const std::string text)
 : value(std::move(text)) // "move" text into value; this code
 { … } // doesn't do what it seems to!
 
 …
private:
 std::string value;
};
           

這裡看似是把text通過std::move轉換成了右值,然後調用string 的移動構造函數。

其實不然,注意到text是有const屬性的,經std::move後它的const屬性并沒有丢掉,而string的移動構造函數形參為string&& rhs,沒法接收const string &&作為實參,是以這裡并不會調用移動構造函數。

反而是拷貝構造函數的形參為const string& rhs,可以接收const string &&,故最後會調用拷貝構造函數。

class string { // std::string is actually a
public: // typedef for std::basic_string<char>
 …
 string(const string& rhs); // copy ctor
 string(string&& rhs); // move ctor
 …
};
           

以上給了我們兩點啟發:

(1)如果想移動一個對象,不要把它設為const.因為對一個const對象的move請求會變成拷貝操作。

(2)std::move實際上不僅不移動任何東西,它還不保證轉換後的結果能夠被移動,它唯一保證的事情是轉換的結果是右值。

std:forward

主要使用場景是一個函數模闆形參為萬能引用,且函數的功能是将該形參傳遞給其他函數,比如:

void process(const Widget& lvalArg);
void process(Widget&& rvalArg);

template<typename T>
void logAndProcess(T&& param){
	auto now=
		std::chrono::system_clock::now();
	makeLogEntry("Calling 'process'",now);
	process(std::forward<T>(param));
}
           

考慮分别傳給logAndProcess左值和右值會發生什麼

Widget w;
logAndProcess(w); // call with lvalue
logAndProcess(std::move(w)); // call with rvalue
           

傳w,是左值,則會調用

void process(const Widget& lvalArg)

;

傳std::move(w),是右值,我們想讓它調用

void process(Widget&& rvalArg)

;但param在函數内是左值,會導緻仍然調用

void process(const Widget& lvalArg)

,std::forward的作用就是,當param傳進來的是右值時,保證傳走的時候還是右值。至于怎麼實作的,我的另一篇部落格有寫https://blog.csdn.net/xxxxxzz123/article/details/115972980。

總的來說,std::move和std::forward在運作時都不做任何事,他們其實就是在做類型轉換,std::move是無條件地把實參轉換為右值,std::forward是僅當傳進來的實參是右值時,轉換為右值(因為傳進來以後都會變成左值,是以需要轉換成右值)。

繼續閱讀