Posted on May 19th, 2009 at 12:03 by fr3@K
喜愛 C++ 並關心 C++0x 標準的朋友一定會知道, C++0x 引進了許多新的元素. 不但對 Standard Library 有為數眾多的 addition 與 enhancement, 連 core language 都新增了不少東西. 而其中最重要的, 在我看來, 就是 Rvalue References. 即便不理會 C++0x 其他的新玩意 Rvalue References 依然是一個職業 C++ programmer 該要掌握的新 feature. 原因很簡單, 正確的運用 Rvalue References, 有可能帶來可觀的 performance gain.
The Stage
兩週前, 在 一篇文字 中提到了一個因為貪玩寫下的 object 字串化/格式化 utility. 並 預告 了將在另一篇文字討論, 在 C++0x 的標準下如何對這個 utility 進行優化. Well, here it is. 先回頭看看原來的 code:
-
template <class T>
-
string str(const T& x)
-
{
-
return boost::lexical_cast<string>(x);
-
}
-
-
template <class T>
-
string operator%(string s, const T& x)
-
{
-
return s += str(x);
-
}
這個 utility 包含了兩個 function template. 使用方法如下:
-
string output = str(123) % ' ' % "abc";
簡單的說, 使用時需先以 function template str 觸發這字串化 utility, 接下來以另一個 function template - operator% - 串接繼續要被字串化的的 object.
The Problem
你知道簡單如 Listing 2 的 use case 會觸發幾次 string 的建構 (與解構) 嗎? 假設每次 lexical_cast 的 invocation 只會觸發一次 string instantiation. 在 compiler 進行了 RVO 之後, 仍有五次 string instantiation.
其中三次是建構從 str/lexical_cast 傳回的 string object,1 另外兩次是 operator% 返回時的 copy construction. 前者可說是無中生有,2 是躲不掉的開銷. 而後者兩次的 construction 雖然一樣是閃不掉, 但在 C++0x 的 Rvalue References 加持下, 其 copy 的 overhead 是可以避開的.
The Move
C++0x 的 Rvalue References 可說是為了達成 move semantics 而誕生的產物. 這裡的 move, 是把一個 object 管理的 resource 轉移到另外一個 object 身上的動作. 而 move construction, 則是把一個 object 的 resource 讓出來, 以創建另一個 object:
-
// a function that returns a temporary string.
-
string unnamed_source();
-
-
void move_construction_demo()
-
{
-
// move-construct a string using unnamed source.
-
string target(unnamed_source());
-
-
string named_source(...);
-
-
// move-construct another string using named source;
-
string another_target(move(named_source));
-
-
// "named_source" is now destructible, but not usable.
-
// Any reference to it may result undefined behavior.
-
}
請參考在之前的文字介紹過的一個字串 class 的 move constructor 以及 move function template 的 definition.
The Solution
有了 Rvalue References, 我們就可以把 Listing 1 的 operator% template 修改成:
-
template <class T>
-
string operator%(string s, const T& x)
-
{
-
return move(s += str(x));
-
}
在這個例子中, 只要在回傳前對 s += str(x) 的結果 - an lvalue (reference to a string) - 呼叫 move, 就能把原本的 copy construction 變成 move construction. 當然也避開了 copy 的開銷.
那為什麼不需要對另外一個 function template - str - 一併做 move? 由於來源是 rvalue (lexical_cast 的傳回值), 而 C++0x 的 string class 也有 move constructor. 在這組合的配合下, move 會自動發生. 即便 string class 沒有 move ctor, 從 lexical_cast 傳回的 string instance 也會被一路 RVO 到 str 的 caller,3 其效果就跟被 move 了是十分接近的.
The Conclusion
C++0x 就要來了. 你能做的最好準備, 就是讓自己熟悉 Rvalue References. 它不是個 fancy feature, 而是能夠實實在在主宰程式的效能的 killer feature.
當 function 是以 return by value 方式傳回一個 named instance, 請記得: "We like to~~ MOVE IT!!!"
Full source code: move-it.cpp.
Compile using g++ 4.3.2: $ g++ -std=c++0x move-it.cpp
![]() |
|
| Previous Post « New Job Openings at Work « |
Next Post » My First JavaScript – Konami Code » |








從語言層面支援的 move 終於快要能用了,真是讓人望穿秋水啊 XD
Comment by av — May 19, 2009 @ 20:48