您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“C++中RVO和NRVO怎么用”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“C++中RVO和NRVO怎么用”這篇文章吧。
和移動操作相關的類函數有兩個
移動構造函數:
A(A&& rhs);
移動賦值運算符:
A& operator=(A&& rhs);
注意這兩個函數的參數類型都不是const
,這也是C++默認會生成的函數聲明。
移動構造函數用于在構造類型的時候使用:
A a1; // 使用std::move強制進行移動 A a2 = std::move(a1); 或 A a2(std::move(a1));
而移動賦值運算符就是在賦值的時候進行移動:
A a1; A a2; a1 = std::move(a2); // 使用move進行強制移動
隱式的移動構造函數將會在可以被生成且滿足如下所有條件的情況下自動生成:
沒有用戶聲明的 復制構造函數
沒有用戶聲明的 復制賦值運算符(即operator
=(const A&)這類)
沒有用戶聲明的 移動賦值運算符(即operator
=(A&&)這類)
沒有用戶聲明的 析構函數
所謂可以被生成的意思是滿足以下所有條件:
類中沒有不能移動的非靜態成員
繼承時,基類可以被移動
繼承時,基類的構造函數可以被訪問
而移動賦值運算符的產生條件也差不多,只不過將沒有聲明的 移動賦值構造函數改成沒有用戶聲明 移動構造函數即可。
總之,這兩個函數生成的條件就一句話:除了普通的構造函數外(指默認構造函數和帶其他參數的構造函數),不得聲明任何其他的構造函數,operator
=函數和析構函數。
使用std::move
是一種強制的,顯式的移動。但是C++很多時候為了效率會自動幫我們移動。主要的規則其實就是所有的右值都會進行移動,如果不能移動,進行拷貝。但是為了嚴謹,我們還是擺出cppreference
上的規則:
初始化的時候使用std::move():T a = std::move(b)
或者T a(std::move(b));
這種。這里要加上std::move(),
不然會調用復制構造函數。
函數實參傳遞的時候使用std::move() :func(std::move(a))
函數返回時,如:
class A {}; A CreateA() { return A(); } // call A a = CreateA();
的時候,使用A()產生的變量會首先移動到CreateA()
函數產生的返回值中,這個時候這個返回值是一個臨時變量(我們記為temp
),接下來就是執行這段代碼:A a = temp
,然后temp是臨時變量, 會再次調用A的移動構造函數給a變量。
前兩個是屬于顯式的移動,最后一種就是隱式移動。移動賦值運算符的規則也是一樣,只有等號右邊是臨時變量就會自動調用。
雖然C++對移動操作定義的很明確,但編譯器卻并不總是按照這個定義去做。因為編譯器中有三個重要的優化經常會減少拷貝,甚至是移動操作。
在GCC
和Clang
下可以添加-fno-elide-constructors
選項來關閉這三種優化。
來看一看下面代碼:
class C { public: C() {} C(const C&) { std::cout << "A copy was made.\n"; } C(C&& rhs) { std::cout << "A move was made.\n"; } }; C f() { return C(); } int main() { std::cout << "Hello World!\n"; C obj = f(); }
這里建議在C++17標準下編譯,因為C++17起所有的復制消規則除被寫在語言規范內,大部分編譯器應該都會做這件事。我的Clang++ 12.0.5上的執行結果僅僅是輸出了一行Hello World:
Hello World!
按照上面的規則,函數在返回的時候會進行移動,也就是說在f()的調用內,會先移動給臨時變量,然后臨時變量再移動給obj,但是這里什么都沒發生,沒有任何的移動和拷貝,obj就像憑空出現了一樣。
在C++17起,復制消除是強制執行的,而C++11中是看編譯器心情。
在如下條件下會進行復制消除:
在return
語句中,return
的值是和函數返回值類型一樣的右值。類型一樣是為了防止隱式轉換,否則會產生新的變量從而阻止移動,右值是因為C++自動移動只能對右值操作。
在變量初始化的時候,初始化表達式是右值。如:
class A{}; A f() { return A(); } // 這里是第一種情況,會自動復制消除 // call A a = f(); // 這里函數返回值的臨時變量到a的過程中的移動也會被消除
這也就解釋了為什么上面的代碼沒有調用任何的拷貝,移動函數了。
RVO是Return Value Optimization
(返回值優化)的簡寫,而NRVO
是Named Return Value Optimization
(命名返回值優化)的簡寫。這兩個優化是復制消除的常見形式。
通過他們的名字就可以看出,這是在函數返回的時候做的優化。
RVO是指在函數返回一個臨時變量時的優化,具體的優化如下:
// 原本的函數 T CreateT(int value) { return T(value); } T a = CreateT(10); // 優化后的函數(偽代碼): void CreateT(T& v, int value) { v.T::T(value); // 直接在內部進行構造 }
即通過將要接收函數返回值的對象以引用的形式放入函數內部初始化,這樣就避免了一次移動/拷貝。
而NRVO則是更加寬泛的RVO。對于如下的代碼可以執行NRVO:
T CreateT(int values) { T t(value); return t; }
編譯器也會優化成上面RVO優化的樣子。
以上是“C++中RVO和NRVO怎么用”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。