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 constructor
Foo(const Foo& a) : x(a.x),
y(a.y) {
// Deep copy
copyStringValue(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。
相關文章
- Google C++ Coding Style:引用引數GoC++
- C++右值引用C++
- C++左值引用與右值引用C++
- c++ 左值引用與右值引用C++
- C++ 右值引用和左值引用C++
- C++ 11 中的右值引用C++
- 右值引用
- 左值、右值、左值引用,右值引用,std::move函式函式
- c++11之左值引用和右值引用C++
- Google C++ style guide——格式GoC++GUIIDE
- C++11/14::右值引用C++
- C++ 左值和右值C++
- C++ :引用計數(reference count) 實現C++
- C++11中的右值引用C++
- Google C++ Style Guide的哲學GoC++GUIIDE
- C++11 左值引用和右值引用與引用摺疊和完美轉發C++
- C++智慧指標之shared_ptr與右值引用(詳細)C++指標
- c++中的左值與右值C++
- 一段小程式碼秒懂C++右值引用和RVO(返回值優化)的誤區C++優化
- vim Google style formatGoORM
- Git Reference引用詳解Git
- Google 是如何審批20億行程式碼的?coding style 真的很重要!Go行程
- C++ 引用 (交換兩個數的值)C++
- C++11 中的右值引用與轉移語義C++
- C++左值右值完美轉發轉移C++
- C++ coding standardC++
- C++霧中風景10:聊聊左值,純右值與將亡值C++
- oracle reference partition引用分割槽(一)Oracle
- c++11-17 模板核心知識(十)—— 區分萬能引用(universal references)和右值引用C++
- 透徹理解C++11新特性:右值引用、std::move、std::forwardC++Forward
- JavaScript 左值與右值JavaScript
- 【C++】引用C++
- c++引用C++
- C++ 引用C++
- [c++11]我理解的右值引用、移動語義和完美轉發C++
- C++移動語義與右值引用完美展示與原理介紹 更新於2018-05-01C++
- 方法引用(Method reference)和invokedynamic指令詳細分析
- [SceneKit專題]11-Reference-Nodes引用節點