Google C++ Coding Style:右值引用(Rvalue Reference)
右值引用是一個C++11特性,標記為T&&。GSG中定義:只為移動建構函數(Move constructor)和移動賦值操作(Move assignment)使用右值引用。并且不要使用std::Forward(提供的完美轉發特性)。
C++中右值指表達式結束時就不再存的臨時對象。在C++11中,右值分為純右值(即原始字面量,表達式產生的臨時變量等),以及一個將亡值(expiring value, 使用<<深入應用C++11>>中的譯法,指的是與右值引用相關的表達式,如將被移動的對象,T&&函數返回值等)。
以函數返回值表達不出右值引用的威力,因為編譯的本身的優化會解決不必要的對象復制操作。而作為函數參數,如果使用const T&之類的形式也能夠有效避免不必要的對象拷貝。這里特別以與標準容器配合,體現一下,右值引用最大的價值:避免深拷貝。
// 下面一個完整提供了Move Constructor和Move assignment的類。 #include <iostream> #include <string> #include <vector> #include <string.h>class Foo {private:int x = 0;int y = 0;char* strPtr = nullptr;public:Foo() {std::cout << "Constructor was called." << std::endl;}Foo(const char* s) {std::cout << "Constructor with string:" << s << std::endl;if (s != nullptr) {strPtr = new char[strlen(s)];strcpy(strPtr, s);}}// Copy constructorFoo(const Foo& a) : x(a.x),y(a.y) {// Deep copycopyStringValue(a.strPtr);std::cout << "Copy constructor was called." << std::endl;}// Move constructor, no need copy string in deep.Foo(Foo&& a) : x(a.x),y(a.y),strPtr(a.strPtr) {a.strPtr = nullptr; // 注意要清掉之前的字串,這樣才是移動。std::cout << "Move constructor was called." << std::endl;}Foo& operator=(const Foo& a) {x = a.x;y = a.y;copyStringValue(a.strPtr);std::cout << "Assignment Operator was called." << std::endl;}~Foo() {if (strPtr != nullptr) {std::cout << "Free allocated string:" << strPtr << std::endl;delete strPtr;}std:: cout << "Deconstructor was called." << std::endl;}private:void copyStringValue(const char* s) {if (strPtr != nullptr) {delete strPtr;strPtr = nullptr;}if (s != nullptr) {strPtr = new char[strlen(s)];strcpy(strPtr, s);}} };int main(void) {{std::cout << "Need to clear string twice:" << std::endl;std::vector<Foo> myVec;Foo a("Instance A");myVec.push_back(a);}std::cout << "============" << std::endl;{std::cout << "Only need to clear string one time:" << std::endl;std::vector<Foo> myVec;Foo c("Instance C");myVec.push_back(std::move(c));}std::cout << "============" << std::endl;{Foo d("Instance D");Foo x = d;}std::cout << "============" << std::endl;{Foo e("Instance E");Foo&& y = std::move(e);} }觀察代碼刪除字串的次數,就可以了解右值引用的作用了。程序運行的輸出如下:
Need to clear string twice: Constructor with string:Instance A Copy constructor was called. Free allocated string:Instance A Deconstructor was called. Free allocated string:Instance A Deconstructor was called. ============ Only need to clear string one time: Constructor with string:Instance C Move constructor was called. Deconstructor was called. Free allocated string:Instance C Deconstructor was called. ============ Constructor with string:Instance D Copy constructor was called. Free allocated string:Instance D Deconstructor was called. Free allocated string:Instance D Deconstructor was called. ============ Constructor with string:Instance E Free allocated string:Instance E Deconstructor was called.但是考慮到右值引用中的引用折疊(reference collapsing)會引入一些復雜度(左右值的轉換規則),造成理解上的問題,所以將右值引用的應用范圍做了如開篇所說的限定。
在實際應用中,會出現沒有直接定義類型的右值引用,被稱為universal reference,需要進行類型推導。另一種情況是使用auto &&定義的也是universal reference。
關于std::forward,它被稱為完美轉發(Perfect Forwarding)。要解決的問題是在函數模板中,完全依照模板的參數的類型,保持參數的左值,右值特征),將參數傳遞給函數模板中調用的另一個函數(轉自<<深入應用C++11>>)。根據這個定義,完美轉發僅針對需要調用內部實現的模板函數,而且需要開發者識別出哪些情況是有效的,而哪些情況下又是無效的。比如適用的場景:
template<class T> void foo(T&& arg) {// 如下保持arg的類型傳入到bar()中bar(std::forward<T>(arg)); }但如果內部函數無需要針對左值或右值做特殊處理,這種場景是不需要轉發的。參考:When not to use std::forward。
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的Google C++ Coding Style:右值引用(Rvalue Reference)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jquery easyui datagr
- 下一篇: PreparedStatement批量处