【譯】VC10中的C++0x特性 part 2(2):右值引用
來(lái)源:
vcblog 作者:Stephan T. Lavavej 翻譯:
飄飄白云
(轉(zhuǎn)載時(shí)請(qǐng)注明作者和出處。未經(jīng)許可,請(qǐng)勿用于商業(yè)用途)
簡(jiǎn)介
這一系列文章介紹Microsoft Visual Studio 2010 中支持的C++ 0x特性,目前有三部分。
Part 1 :介紹了Lambdas, 賦予新意義的auto,以及 static_assert;
Part 2( 一 , 二 , 三 ):介紹了右值引用(Rvalue References);
Part 3:介紹了表達(dá)式類(lèi)型(decltype)
VC10中的C++0x特性 Part 1,2,3 譯文打包下載(doc 和 pdf 格式): 點(diǎn)此下載
本文為 Part 2 第二頁(yè)。
move 語(yǔ)意:從 lvalue 移動(dòng)
現(xiàn)在,如果你喜歡用拷貝賦值函數(shù)來(lái)實(shí)現(xiàn)你的拷貝構(gòu)造函數(shù)該怎樣做呢,那你也可能試圖用 move 拷貝賦值函數(shù)來(lái)實(shí)現(xiàn) move 構(gòu)造函數(shù)。這樣作是可以的,但是你得小心。下面就是一個(gè)錯(cuò)誤的實(shí)現(xiàn):
C:\Temp>type unified_wrong.cpp
#include <stddef.h>
#include <iostream>
#include <ostream>
using namespace std;
class remote_integer {
public:
remote_integer() {
cout << "Default constructor." << endl;
m_p = NULL;
}
explicit remote_integer(const int n) {
cout << "Unary constructor." << endl;
m_p = new int(n);
}
remote_integer(const remote_integer& other) {
cout << "Copy constructor." << endl;
m_p = NULL;
*this = other;
}
#ifdef MOVABLE
remote_integer(remote_integer&& other) {
cout << "MOVE CONSTRUCTOR." << endl;
m_p = NULL;
*this = other; // WRONG
}
#endif // #ifdef MOVABLE
remote_integer& operator=(const remote_integer& other) {
cout << "Copy assignment operator." << endl;
if (this != &other) {
delete m_p;
if (other.m_p) {
m_p = new int(*other.m_p);
} else {
m_p = NULL;
}
}
return *this;
}
#ifdef MOVABLE
remote_integer& operator=(remote_integer&& other) {
cout << "MOVE ASSIGNMENT OPERATOR." << endl;
if (this != &other) {
delete m_p;
m_p = other.m_p;
other.m_p = NULL;
}
return *this;
}
#endif // #ifdef MOVABLE
~remote_integer() {
cout << "Destructor." << endl;
delete m_p;
}
int get() const {
return m_p ? *m_p : 0;
}
private:
int * m_p;
};
remote_integer frumple(const int n) {
if (n == 1729) {
return remote_integer(1729);
}
remote_integer ret(n * n);
return ret;
}
int main() {
remote_integer x = frumple(5);
cout << x.get() << endl;
remote_integer y = frumple(1729);
cout << y.get() << endl;
}
C:\Temp>cl /EHsc /nologo /W4 /O2 unified_wrong.cpp
unified_wrong.cpp
C:\Temp>unified_wrong
Unary constructor.
Copy constructor.
Copy assignment operator.
Destructor.
25
Unary constructor.
1729
Destructor.
Destructor.
C:\Temp>cl /EHsc /nologo /W4 /O2 /DMOVABLE unified_wrong.cpp
unified_wrong.cpp
C:\Temp>unified_wrong
Unary constructor.
MOVE CONSTRUCTOR.
Copy assignment operator.
Destructor.
25
Unary constructor.
1729
Destructor.
Destructor.
(編譯器在這里進(jìn)行了返回值優(yōu)化(RVO),但不是具名返回值優(yōu)化(NRVO)。就像我之前提到的,有些拷貝構(gòu)造函數(shù)被 RVO 或 NRVO 優(yōu)化掉了,但編譯器并不總是能夠做這樣的優(yōu)化,這時(shí)剩余的就由 move 構(gòu)造函數(shù)來(lái)優(yōu)化。)
move 構(gòu)造函數(shù)中標(biāo)記為 WRONG 的那一行,調(diào)用了拷貝賦值函數(shù),編譯能通過(guò)也能運(yùn)行,但這違背了 move 構(gòu)造函數(shù)的本意。(譯注:因?yàn)槟莻€(gè)拷貝賦值函數(shù)只是進(jìn)行普通的拷貝賦值,而不是 move 賦值!)
這是怎么回事呢?記住:在C++98/03中,具名 lvalue 引用是左值(給定語(yǔ)句 int& r = *p; r 是 lvalue),不具名 lvalue 引用還是左值(給定語(yǔ)句 vector<int> v(10, 1729), v[0] 返回 int&, 你可以對(duì)這個(gè)不具名 lvalue 引用取址)。但是 rvalue 引用就不一樣了:
? 具名 lvalue 引用是 lvalue。
? 不具名 rvalue 引用是 rvalue。
一個(gè)具名 rvalue 引用是一個(gè) lvalue 是因?yàn)榭梢詫?duì)它施加多重操作,重復(fù)使用。相反,如果它是一個(gè) ravlue 的話,那么對(duì)它施加的第一個(gè)操作能夠“竊取”它,而后續(xù)操作就沒(méi)機(jī)會(huì)了。這里的“竊取”是說(shuō)不會(huì)被察覺(jué)到,所以這是行不通的。另一方面,不具名 rvalue 引用不能被重復(fù)使用,所以它仍保持右值(rvalueness)語(yǔ)意。
如果你真的打算用 move 賦值函數(shù)來(lái)實(shí)現(xiàn) move 構(gòu)造函數(shù),你需要從 lvalue move,就像是從 rvalue move 一樣。C++0x <utility> 中的 std::move() 具備這樣的能力,VC10將會(huì)有這個(gè)(實(shí)際上,開(kāi)發(fā)版中已經(jīng)有了),但VC10 TCP版還沒(méi)有,所以我會(huì)教你從頭做起:
C:\Temp>type unified_right.cpp
#include <stddef.h>
#include <iostream>
#include <ostream>
using namespace std;
template <typename T> struct RemoveReference {
typedef T type;
};
template <typename T> struct RemoveReference<T&> {
typedef T type;
};
template <typename T> struct RemoveReference<T&&> {
typedef T type;
};
template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {
return t;
}
class remote_integer {
public:
remote_integer() {
cout << "Default constructor." << endl;
m_p = NULL;
}
explicit remote_integer(const int n) {
cout << "Unary constructor." << endl;
m_p = new int(n);
}
remote_integer(const remote_integer& other) {
cout << "Copy constructor." << endl;
m_p = NULL;
*this = other;
}
#ifdef MOVABLE
remote_integer(remote_integer&& other) {
cout << "MOVE CONSTRUCTOR." << endl;
m_p = NULL;
*this = Move(other); // RIGHT
}
#endif // #ifdef MOVABLE
remote_integer& operator=(const remote_integer& other) {
cout << "Copy assignment operator." << endl;
if (this != &other) {
delete m_p;
if (other.m_p) {
m_p = new int(*other.m_p);
} else {
m_p = NULL;
}
}
return *this;
}
#ifdef MOVABLE
remote_integer& operator=(remote_integer&& other) {
cout << "MOVE ASSIGNMENT OPERATOR." << endl;
if (this != &other) {
delete m_p;
m_p = other.m_p;
other.m_p = NULL;
}
return *this;
}
#endif // #ifdef MOVABLE
~remote_integer() {
cout << "Destructor." << endl;
delete m_p;
}
int get() const {
return m_p ? *m_p : 0;
}
private:
int * m_p;
};
remote_integer frumple(const int n) {
if (n == 1729) {
return remote_integer(1729);
}
remote_integer ret(n * n);
return ret;
}
int main() {
remote_integer x = frumple(5);
cout << x.get() << endl;
remote_integer y = frumple(1729);
cout << y.get() << endl;
}
C:\Temp>cl /EHsc /nologo /W4 /O2 /DMOVABLE unified_right.cpp
unified_right.cpp
C:\Temp>unified_right
Unary constructor.
MOVE CONSTRUCTOR.
MOVE ASSIGNMENT OPERATOR.
Destructor.
25
Unary constructor.
1729
Destructor.
Destructor.
(我將交替使用 std::move() 和我自己的 Move(),因?yàn)樗鼈兊膶?shí)現(xiàn)是等價(jià)的) std::move() 是怎樣工作的呢?目前,我只能跟你說(shuō)這是“魔法”。(后面會(huì)有完整的解釋?zhuān)⒉粡?fù)雜,但它與模板參數(shù)推導(dǎo)和引用折疊(reference collapsing,譯注:引用的引用)有 關(guān),后面講完美轉(zhuǎn)發(fā)的時(shí)候我們還會(huì)遇到這兩個(gè)東西)。我可以用一個(gè)具體的例子來(lái)略過(guò)“魔法”:給定一個(gè) string 類(lèi)型的左值,像前面重載決議例子中的 up ,std::move(up) 調(diào)用 string&& std::move(string&),這個(gè)函數(shù)返回一個(gè)不具名的 rvalue 引用,它是一個(gè) rvalue。給定一個(gè) string 類(lèi)型的 rvalue,像前面重載決議例子中的 strange(), std::move(strange()) 調(diào)用 string&& std::move(string&&),同樣這個(gè)函數(shù)還是返回一個(gè)不具名的 rvalue,還是 rvalue。
std::move() 除了讓你能用 move 復(fù)制函數(shù)來(lái)實(shí)現(xiàn) move 構(gòu)造函數(shù)之外,還能在其他地方發(fā)揮作用。無(wú)論何時(shí),只要你有一個(gè)左值,而它的值也不再重要了(例如,它將被銷(xiāo)毀或被賦值),你就可以使用 std::move(你的左值表達(dá)式) 來(lái)使用 move 語(yǔ)意。
move 語(yǔ)意:可移動(dòng)成員(movable member)
C++0x 的標(biāo)準(zhǔn)類(lèi)型(像 vector, string, regex) 都有 move 構(gòu)造函數(shù)和 move 賦值函數(shù)。而且我們也已經(jīng)看到了如何在我們自己的類(lèi)中通過(guò)手動(dòng)管理資源來(lái)實(shí)現(xiàn) move 語(yǔ)意(像前面的 remote_integer 類(lèi))。如果類(lèi)中包含可移動(dòng)數(shù)據(jù)成員(像 vector, string, regex, remote_integer )時(shí)該怎么辦呢?編譯器不會(huì)自動(dòng)幫我們自動(dòng)產(chǎn)生 move 構(gòu)造函數(shù)和 move 賦值函數(shù),所以我們必須手動(dòng)編寫(xiě)它們。很幸運(yùn),有了 std::move() 編寫(xiě)它們是很容易的。
C:\Temp>type point.cpp
#include <stddef.h>
#include <iostream>
#include <ostream>
using namespace std;
template <typename T> struct RemoveReference {
typedef T type;
};
template <typename T> struct RemoveReference<T&> {
typedef T type;
};
template <typename T> struct RemoveReference<T&&> {
typedef T type;
};
template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {
return t;
}
class remote_integer {
public:
remote_integer() {
cout << "Default constructor." << endl;
m_p = NULL;
}
explicit remote_integer(const int n) {
cout << "Unary constructor." << endl;
m_p = new int(n);
}
remote_integer(const remote_integer& other) {
cout << "Copy constructor." << endl;
if (other.m_p) {
m_p = new int(*other.m_p);
} else {
m_p = NULL;
}
}
remote_integer(remote_integer&& other) {
cout << "MOVE CONSTRUCTOR." << endl;
m_p = other.m_p;
other.m_p = NULL;
}
remote_integer& operator=(const remote_integer& other) {
cout << "Copy assignment operator." << endl;
if (this != &other) {
delete m_p;
if (other.m_p) {
m_p = new int(*other.m_p);
} else {
m_p = NULL;
}
}
return *this;
}
remote_integer& operator=(remote_integer&& other) {
cout << "MOVE ASSIGNMENT OPERATOR." << endl;
if (this != &other) {
delete m_p;
m_p = other.m_p;
other.m_p = NULL;
}
return *this;
}
~remote_integer() {
cout << "Destructor." << endl;
delete m_p;
}
int get() const {
return m_p ? *m_p : 0;
}
private:
int * m_p;
};
class remote_point {
public:
remote_point(const int x_arg, const int y_arg)
: m_x(x_arg), m_y(y_arg) { }
remote_point(remote_point&& other)
: m_x(Move(other.m_x)),
m_y(Move(other.m_y)) { }
remote_point& operator=(remote_point&& other) {
m_x = Move(other.m_x);
m_y = Move(other.m_y);
return *this;
}
int x() const { return m_x.get(); }
int y() const { return m_y.get(); }
private:
remote_integer m_x;
remote_integer m_y;
};
remote_point five_by_five() {
return remote_point(5, 5);
}
remote_point taxicab(const int n) {
if (n == 0) {
return remote_point(1, 1728);
}
remote_point ret(729, 1000);
return ret;
}
int main() {
remote_point p = taxicab(43112609);
cout << "(" << p.x() << ", " << p.y() << ")" << endl;
p = five_by_five();
cout << "(" << p.x() << ", " << p.y() << ")" << endl;
}
C:\Temp>cl /EHsc /nologo /W4 /O2 point.cpp
point.cpp
C:\Temp>point
Unary constructor.
Unary constructor.
MOVE CONSTRUCTOR.
MOVE CONSTRUCTOR.
Destructor.
Destructor.
(729, 1000)
Unary constructor.
Unary constructor.
MOVE ASSIGNMENT OPERATOR.
MOVE ASSIGNMENT OPERATOR.
Destructor.
Destructor.
(5, 5)
Destructor.
Destructor.
現(xiàn)在你看到啦,按成員移動(dòng)(memberwise move)是很容易做到的。注意, remote_point 的 move 賦值函數(shù)沒(méi)有進(jìn)行自我賦值檢查,是因?yàn)?remote_integer 已經(jīng)檢查過(guò)了。也要注意到 remote_point 隱式聲明的拷貝構(gòu)造函數(shù),拷貝賦值函數(shù)和析構(gòu)函數(shù)都正常運(yùn)作。
到現(xiàn)在,你應(yīng)該對(duì) move 語(yǔ)意已經(jīng)非常熟悉了。(希望不是抓狂啊!)為了測(cè)試你新獲得的這個(gè)不可思議的技能,請(qǐng)為前面的例子寫(xiě)一個(gè) +() 操作符函數(shù)當(dāng)作練習(xí)吧。
最后的提醒:只要你的類(lèi)支持 move 語(yǔ)意,你就應(yīng)該實(shí)現(xiàn) move 構(gòu)造函數(shù)和 move 賦值函數(shù)。因?yàn)椴粌H僅是你平常使用這些類(lèi)時(shí)可從 move 語(yǔ)意中獲利, STL 容器和算法也能從中獲利,通過(guò)廉價(jià)的 move 省下昂貴的拷貝開(kāi)銷(xiāo)。
(轉(zhuǎn)載請(qǐng)注明出處,作者與譯者信息,請(qǐng)勿用于商業(yè)用途)
< 前一頁(yè) 后一頁(yè) >