c++11 auto 與 decltype 詳解

weixin_30924079發表於2020-04-04

轉自: here

一. auto簡介

程式設計時候常常需要把表示式的值付給變數,需要在宣告變數的時候清楚的知道變數是什麼型別。然而做到這一點並非那麼容易(特別是模板中),有時候根本做不到。為了解決這個問題,C++11新標準就引入了auto型別說明符,用它就能讓編譯器替我們去分析表示式所屬的型別。和原來那些只對應某種特定的型別說明符(例如 int)不同。auto 讓編譯器通過初始值來進行型別推演。從而獲得定義變數的型別,所以說 auto 定義的變數必須有初始值。

 

[cpp] view plain copy
 
  1. //由val_1 和val_2相加的結果可以推斷出item的型別  
  2. auto item = val_1 + val_2;//item 型別初始化為val_1 + val_2相加後的型別,值為val_1+val_2相加的值。  



 

    這裡的 item 的型別是編譯器在編譯的過程中通過val_1和val_2的型別相加後推算出來的。假如是val_1(int) + val_2(double),那麼item的型別就是double.

            使用auto也能在一個語句中宣告多個變數,因為一個宣告雨具只能有一個基本資料型別,所以該雨具所有變數的初始基本資料型別都必須是一樣的。在這裡一定要區別資料型別和型別修飾符!!

 

[cpp] view plain copy
 
  1. int i = 3;  
  2. auto a = i,&b = i,*c = &i;//正確: a初始化為i的副本,b初始化為i的引用,c為i的指標.  
  3. auto sz = 0, pi = 3.14;//錯誤,兩個變數的型別不一樣。  


            編譯器推斷出來的auto型別有時候會跟初始值的型別並不完全一樣,編譯器會適當的改變結果型別使得其更符合初始化規則。

 

            首先,正如我們熟知的,使用引用其實是使用引用的物件,特別當引用被用作初始值的時候,真正參與初始化的其實是引用物件的值。此時編譯器以引用物件的型別作為auto的型別:

 

[cpp] view plain copy
 
  1. int i = 0 ,&r = i;//定義一個整數i,並且定義r為i的應用.  
  2. auto a = r; //這裡的a為為一個整數,其值跟此時的i一樣.  


            由此可以看出auto會忽略引用,其次,auto一般會忽略掉頂層const,但底層const會被保留下來,比如當初始值是一個指向常量的指標時:

 

 

[cpp] view plain copy
 
  1. int i = 0;  
  2. const int ci = i, &cr = ci;  //ci 為整數常量,cr 為整數常量引用   
  3. auto a = ci;     // a 為一個整數, 頂層const被忽略  
  4. auto b = cr;     // b 為一個整數,頂層const被忽略  
  5. auto c = &ci;    // c 為一個整數指標.  
  6. auto d = &cr;    // d 為一個指向整數常量的指標(對常量物件區地址是那麼const會變成底層const)  


            如果你希望推斷出auto型別是一個頂層的const,需要明確指出:

 

 

[cpp] view plain copy
 
  1. const auto f = ci;  


            還可以將引用的型別設為auto,此時原來的初始化規則仍然適用(用於引用宣告的const都是底層const):

 

 

[cpp] view plain copy
 
  1. auto &g = ci; //g是一個整數常量引用,繫結到ci。  
  2. auto &h = 42; // 錯誤:非常量引用的初始值必須為左值。  
  3. const auto &j = 42; //正確:常量引用可以繫結到字面值。   



 

二. decltype簡介

 

            有的時候我們還會遇到這種情況,我們希望從表示式中推斷出要定義變數的型別,但卻不想用表示式的值去初始化變數。還有可能是函式的返回型別為某表示式的的值型別。在這些時候auto顯得就無力了,所以C++11又引入了第二種型別說明符decltype,它的作用是選擇並返回運算元的資料型別。在此過程中,編譯器只是分析表示式並得到它的型別,卻不進行實際的計算表示式的值。

 

[cpp] view plain copy
 
  1. decltype(f()) sum = x;// sum的型別就是函式f的返回值型別。  

          

 

            在這裡編譯器並不實際呼叫f函式,而是分析f函式的返回值作為sum的定義型別。

            基本上decltype的作用和auto很相似,就不一一列舉了。對於decltype還有一個用途就是在c++11引入的後置返回型別。

 

三. decltype 和 auto 區別

 

            decltype在處理頂層const和引用的方式與auto有些許不同,如果decltype使用的表示式是一個變數,則decltype返回該變數的型別(包括頂層const和引用在內)。

 

[cpp] view plain copy
 
  1. const int ci = 42, &cj = ci;  
  2.   
  3. decltype(ci) x = 0;   // x 型別為const int  
  4. auto z = ci;          // z 型別為int  
  5.   
  6. decltype(cj) y = x;   // y 型別為const int&  
  7. auto h = cj;          // h 型別為int  


           decltype還有一些值得注意的地方,我們先來看看下面這段程式碼:

 

 

[cpp] view plain copy
 
  1. int i = 42, *p = &i, &r = i;  
  2.   
  3. decltype(i) x1 = 0;       //因為 i 為 int ,所以 x1 為int  
  4. auto x2 = i;              //因為 i 為 int ,所以 x2 為int  
  5.   
  6. decltype(r) y1 = i;       //因為 r 為 int& ,所以 y1 為int&  
  7. auto y2 = r;              //因為 r 為 int& ,但auto會忽略引用,所以 y2 為int  
  8.   
  9. decltype(r + 0) z1 = 0;   //因為 r + 0 為 int ,所以 z1 為int,  
  10. auto z2 = r + 0;          //因為 r + 0 為 int ,所以 z2 為int,  
  11.   
  12. decltype(*p) h1 = i;      //這裡 h1 是int&, 原因後面講  
  13. auto h2 = *p;             // h2 為 int.  


           如果表示式的內容是解引用操作,則decltype將得到引用型別。正如我們所熟悉的那樣,解引用指標可以得到指標所指物件,而且還可以給這個物件賦值。因此decltype(*p)的結果型別就是int&.

 

           decltype和auto還有一處重要的區別是,decltype的結果型別與表達形式密切相關。有一種情況需要特別注意:對於decltype 所用表示式來說,如果變數名加上一對括號,則得到的型別與不加上括號的時候可能不同。如果decltype使用的是一個不加括號的變數,那麼得到的結果就是這個變數的型別。但是如果給這個變數加上一個或多層括號,那麼編譯器會把這個變數當作一個表示式看待,變數是一個可以作為左值的特殊表示式,所以這樣的decltype就會返回引用型別:

 

[cpp] view plain copy
 
  1. int i = 42;  
  2.   
  3. //decltype(i)   int  型別  
  4. //decltype((i)) int& 型別  

 

 

           這裡再指出一個需要注意的地方就是 = 賦值運算子返回的是左值的引用。換句話意思就是說 decltype(i = b)  返回型別為 i 型別的引用。仔細看下面這段程式碼:

 

[cpp] view plain copy
 
  1. int main()  
  2. {  
  3.     int i = 42;  
  4.   
  5.     decltype(i = 41) x = i;  
  6.   
  7.     auto y = i;  
  8.   
  9.     auto& z = i;  
  10.   
  11.     printf("i x y z 此時為: %d %d %d %d\n", i,x,y,z);  
  12.   
  13.     i--;  
  14.   
  15.     printf("i x y z 此時為: %d %d %d %d\n", i, x, y, z);  
  16.   
  17.     x--;  
  18.   
  19.     printf("i x y z 此時為: %d %d %d %d\n", i, x, y, z);  
  20.   
  21.     y--;  
  22.   
  23.     printf("i x y z 此時為: %d %d %d %d\n", i, x, y, z);  
  24.   
  25.     z--;  
  26.   
  27.     printf("i x y z 此時為: %d %d %d %d\n", i, x, y, z);  
  28.   
  29.     return 0;  
  30. }  


執行結果為:

 

i x y z 此時為: 42 42 42 42
i x y z 此時為: 41 41 42 41
i x y z 此時為: 40 40 42 40
i x y z 此時為: 40 40 41 40
i x y z 此時為: 39 39 41 39

     
           由上面的程式碼和執行結果可以看出來,1.decltype(i = 41)中的賦值語句並沒有真正的執行。2. decltype(i = 41)返回的其實是int&,也就是說x 其實是 i 的引用。

 

 

 

瞭解了auto 和 decltype後,以後在使用的過程中一定要分清兩者的區別,防止在定義的時候產生const 與非const 以及引用 非引用 的差別!!

 

轉載於:https://www.cnblogs.com/chenhuan001/p/7516638.html

相關文章