cpp const引用和右值引用的區別,std::move(移動語義), std::forward(完美轉發)

引言

  • string& 作爲函數參數的問題
    你在使用cpp過程是否遇到過使用string&作爲函數參數,main調用的時候編譯失敗的問題?
    例如下面這份簡單的代碼

    void func(string& a) {
    }
    int main() {
      	func("12323");
    } 
    

    編譯器提示如下

    [Error] invalid initialization of non-const reference of type ‘std::string& {aka std::basic_string&}’ from an rvalue of type 'const char*

const & 類型

上面的代碼錯誤原因在於string&是一個引用類型,必須要有地址,而“asdfdsf”是const char*類型(是右值,可以理解爲不可修改的值),無法直接轉換到string&

簡單的修改成如下可以編譯通過

void func(const string& a) {
	
}

int main() {
	func("12323");
} 

如何理解func中的 a?

a是一種const string& 類型,調用func會先調用string的構造函數string(const char*)生成一個臨時變量,命名爲a,函數調用結束之後就會銷燬。

&& 類型

還有一個方案,修改爲右值引用類型

void func(string&& a) {

}

int main() {
	func("12323");
} 

三種引用的理解(左值引用 T & ; const引用 const T& ; 右值引用 T&&)

右值簡單可以理解爲沒有明確地址的值(或者是臨時值),常見的右值有整數,浮點數,字符,字符串,例如以下
1,1.23, 2324, ‘c’,“23423535”

int s() {
	return 1234;	
}

int main() {
	int& f = s(); // 編譯失敗,左值引用必須要明確的內存地址
	int&& f = s(); // 通過
	cout << f<< endl;
} 
  • 爲什麼需要右值引用?
    c/c++默認值傳遞,傳遞函數參數過程中如果沒有引用來修飾的話,會複製出一個臨時變量出來,這個變量函數調用結束會銷燬;而普通的引用只能接受有明確地址的變量,而右值引用接受沒有明確地址的變量(例如上面列舉的那些)
    (代碼1)

    void func(string& a) { // 參數普通引用 
        b[0] = 'a'; 
    }
    int main() {
    	func("abcdefg"); // 不合法 
    	string s;
    	func(s); // 合法
    } 
    

    (代碼2)

    void func(string&& a) { // 參數右值引用 
        a[0] = 'a'; 
     }
    int main() {
    	func("abcdefg"); // 合法 
    	string s;
    	func(s); // 不合法
    } 
    
  • 何爲左值引用(T&)
    可以理解爲一個指針,只不過c語言中的指針需要用*來取值,->來進行訪問。引用簡化了這個過程,讓他使用起來更加的方便(和普通的類型一致)
    函數參數傳遞引用相當於傳遞一個指針,可以避免不必要的構造函數。

  • 何爲const引用(const T&)
    可以理解爲const指針,const引用聲明後沒辦法修改內部的值(只能調用非const方法),除此之外const引用支持右值來構造
    string& aa = “21324”; // 不合法 ,因爲"21324"沒有明確的地址
    const string& bb = “sdfdf”; // 合法,會找到合適的構造函數
    b[0] = ‘a’; // 不合法,不可寫入
    cout << b[0] << endl; //合法,可以讀

  • 何爲右值引用 (T &&)
    例如普通的引用 int& a = 1234; 是編譯吧不通過的,因爲1234沒有明確地址空間
    string c;
    string& s = “abcdefg”; //不合法
    string& a = c; //合法,c有明確的地址
    string&& b = c; //不合法,只能用那些沒有明確地址的值來賦值,例如"abcdefg"
    string&& d = “abcdefg”; // 合法

  • const T& 和 T&&右什麼區別?
    兩者幾乎很相似,只不過const T&受到const的限制還有語義上的區別

    • const T&可以既可以接受普通的變量,又可以接受右值,但是操作起來不方便(受到const的限制),如果想要方便的操作的話,需要使用const_cast 把它轉爲普通的引用。

      void func(const string& a) {
      	string& b = const_cast<string&>(a);
      }
      int main() {
      	func("abcdefg");
      } 
      

      使用T&&可以省略去轉換的過程

      void func(const string& a) {
           string& b = const_cast<string&>(a);
           b[0] = 'a'; //合法的 
       }  
       void func(string&& a) {
       	//a是已經被轉換好的。 
       	a[0] = 'a'; // 合法的 
       }
      
    • 語義上,一般const修飾過的變量一般就認爲不可修改。(雖然可以修改但最好不要這麼做)
      如果你想讓你的函數即可接受普通的左值,又可以接受臨時的變量,還想減少不必要的構造函數調用,可以這麼做。

      string& func(string& a) {
          a[0] = 's'; 
          return a;
      }
      string& func(string&& a) {
      	return func(a); // 這這裏調用 void func(string&) 
      }
      
      int main() {
      	cout << func("55555") << endl; // 調用 void func(string&&)
      	string s = "6666";
      	cout << func(s) << endl; // 調用void func(string&) 
      } 
      

std::move (移動語義)

待補充

std::forward(完美轉發)

待補充

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章