C++11 std::move
文章目錄
-
- 概述
- 關鍵字```std::move```概述
- 錯誤用例
- 正确用例1
- 正确用例2
- 總結
概述
本篇部落格根據
深入了解C++11新特性解析與應用
一書中的内容以及自己在使用
std::move
過程中的經驗,總結形成該篇部落格,将它整理形成知識。該書我已經高清書簽版上傳到CSDN,為了防止不過,修改了檔案名稱,下載下傳位址奉上。
深入了解C++11新特性解析與應用
關鍵字 std::move
概述
std::move
在C++11中,标準庫在
<utility>
中提供了一個有用的函數
std::move
,這個函數的名字具有迷惑性,因為實際上std::move并不能夠移動任何東西,它唯一的功能是将一個左值強制轉化為右值引用,繼而我們可以通過右值引用使用該值,以用于移動語義。
下面是
std::move
可能的實作的一種方式。
template< class T >
typename std::remove_reference<T>::type&& move(T&& t) noexcept
{
return static_cast<typename remove_reference<T>::type&&>(t);
}
從上訴代碼中,可以看出
std::move
基本等同于一個類型轉換:
statc_cast<T&&>(lvalue);
錯誤用例
需要注意的是被轉化的值,其生命期并沒有随着左右值的轉換而改變,即被std::move轉化的左值變量
lvalue
并不會被立即析構。
#include <iostream>
using namespace std;
class Moveable{
public:
Moveable():i(new int(3)) {}
~Moveable() { delete i; }
Moveable(const Moveable & m): i(new int(*m.i)) { }
Moveable(Moveable && m):i(m.i) {
m.i = nullptr;
}
int* i;
};
int main() {
Moveable a;
Moveable c(move(a)); // 會調用移動構造函數
cout << *a.i << endl; // 運作時錯誤
}
上述代碼中
a
本來是一個左值變量,通過
std::move
将其轉換為右值。這樣一來,
a.i
就被
c
的移動構造函數設定為指針空值。由于
a
的生命周期要到
main
函數結束才結束,是以對表達式
*a.i
進行計算的時候,就會發生嚴重的運作時錯誤。
上述代碼是典型誤用
std::move
的例子。
正确用例1
要正确使用該函數,必須是程式員清楚需要轉換的時候,比如上例中,程式員應該知道被轉換為右值的
a
不可以再使用。不過更多地,我們需要轉換成為右值引用的還是一個确實聲明期即将結束的對象。下面給出正确的用例。
#include <iostream>
using namespace std;
class HugeMem{
public:
HugeMem(int size): sz(size > 0 ? size : 1) {
c = new int[sz];
}
~HugeMem() { delete [] c; }
HugeMem(HugeMem && hm): sz(hm.sz), c(hm.c) {
hm.c = nullptr;
}
int * c;
int sz;
};
class Moveable{
public:
Moveable():i(new int(3)), h(1024) {}
~Moveable() { delete i; }
Moveable(Moveable && m):
i(m.i), h(move(m.h)) { // 強制轉為右值,以調用移動構造函數
m.i = nullptr;
}
int* i;
HugeMem h;
};
Moveable GetTemp() {
Moveable tmp = Moveable();
cout << hex << "Huge Mem from " << __func__
<< " @" << tmp.h.c << endl; // Huge Mem from GetTemp @0x603030
return tmp;
}
int main() {
Moveable a(GetTemp());
cout << hex << "Huge Mem from " << __func__
<< " @" << a.h.c << endl; // Huge Mem from main @0x603030
}
上述例子我們定義了兩個類型:
HugeMem
和
Moveable
,其中
Moveable
包含了一個
HugeMem
的對象。在
Moveable
的移動構造函數中,我們使用了
std::move
,它将
m.h
強制轉換為右值,以使用
Moveable
中的
h
能夠實作移動構造,由于
GetTemp()
傳回的是右值,是以
m
将在表達式結束後被析構,其成員自然也被析構。
正确用例2
#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
std::string str = "Hello";
std::vector<std::string> v;
// uses the push_back(const T&) overload, which means
// we'll incur the cost of copying str
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n";
// uses the rvalue reference push_back(T&&) overload,
// which means no strings will be copied; instead, the contents
// of str will be moved into the vector. This is less
// expensive, but also means str might now be empty.
v.push_back(std::move(str));
std::cout << "After move, str is \"" << str << "\"\n";
std::cout << "The contents of the vector are \"" << v[0]
<< "\", \"" << v[1] << "\"\n";
}
總結
事實上,為了保證移動語義的傳遞,程式員在編寫移動構造函數的時候,應該總是記得使用
std::move
轉換擁有的形如堆記憶體、檔案句柄等資源的成員為右值,這樣一來,如果成員支援移動構造的話,就可以實作其移動語義。