c++ 之 std::move 與 完美轉發 std::forward
阿新 • • 發佈:2018-12-03
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <vector> #include <map> // C++中還有一個被廣泛認同的說法,那就是可以取地址的、有名字的就是左值,反之,不能取地址的、沒有名字的就是右值。 // 相對於左值,右值表示字面常量、表示式、函式的非引用返回值等。 // 既然編譯器只對右值引用才能呼叫轉移建構函式和轉移賦值函式,而所有命名物件都只能是左值引用, //如果已知一個命名物件不再被使用而想對它呼叫轉移建構函式和轉移賦值函式,也就是把一個左值引用 //當做右值引用來使用,怎麼做呢? // 標準庫提供了函式 std::move,這個函式以非常簡單的方式將左值引用轉換為右值引用。 int a; int &&r1 = a; // 編譯失敗 int &&r2 = std::move(a); // 編譯通過 // 完美轉發 std::forward // 完美轉發適用於這樣的場景:需要將一組引數原封不動的傳遞給另一個函式. // “原封不動”不僅僅是引數的值不變,在 C++ 中,除了引數值之外,還有一下兩組屬性:左值/右值和 // const/non-const。 // 完美轉發就是在引數傳遞過程中,所有這些屬性和引數值都不能改變,同時,而不產生額外的開銷,就好像 //轉發者不存在一樣。在泛型函式中,這樣的需求非常普遍。 // C++11是如何解決完美轉發的問題的呢?實際上,C++11是通過引入一條所謂“引用摺疊”(reference //collapsing)的新語言規則,並結合新的模板推導規則來完成完美轉發。 typedef const int T; typedef T & TR; TR &v = 1; //在C++11中,一旦出現了這樣的表示式,就會發生引用摺疊,即將複雜的未知表示式摺疊為已知的 //簡單表示式 /* C++11中的引用摺疊規則: TR的型別定義 宣告v的型別 v的實際型別 T & TR T & T & TR & T & T & TR && T & T && TR T && T && TR & T & T && TR && T && 一旦定義中出現了左值引用,引用摺疊總是優先將其摺疊為左值引用 */ // C++11中,std::forward可以儲存引數的左值或右值特性 #include <iostream> using namespace std; template <typename T> void process_value(T & val) { cout << "T &" << endl; } template <typename T> void process_value(T && val) { cout << "T &&" << endl; } template <typename T> void process_value(const T & val) { cout << "const T &" << endl; } template <typename T> void process_value(const T && val) { cout << "const T &&" << endl; } //函式 forward_value 是一個泛型函式,它將一個引數傳遞給另一個函式 process_value template <typename T> void forward_value(T && val) //引數為右值引用 { process_value( std::forward<T>(val) ); // C++11中,std::forward可以儲存引數的左值或右值特性 } void mytest() { int a = 0; const int &b = 1; forward_value(a); // T & forward_value(b); // const T & forward_value(2); // T && forward_value( std::move(b) ); // const T && return; } int main() { mytest(); system("pause"); return 0; }