C++ auto和decltype的區別

通過《C++ auto》和《C++ decltype》兩節的學習,相信大家已經掌握了 auto 和 decltype 的語法規則以及使用場景,這節我們將 auto 和 decltype 放在一起,綜合對比一下它們的區別,並告訴大家該如何選擇。

語法格式的區別

auto 和 decltype 都是 C++11 新增的關鍵字,都用於自動類型推導,但是它們的語法格式是有區別的,如下所示:

auto varname = value;  //auto的語法格式
decltype(exp) varname [= value];  //decltype的語法格式

其中,varname 表示變量名,value 表示賦給變量的值,exp 表示一個表達式,方括號[ ]表示可有可無。

auto 和 decltype 都會自動推導出變量 varname 的類型:

  • auto 根據=右邊的初始值 value 推導出變量的類型;

  • decltype 根據 exp 表達式推導出變量的類型,跟=右邊的 value 沒有關係。


另外,auto 要求變量必須初始化,也就是在定義變量的同時必須給它賦值;而 decltype 不要求,初始化與否都不影響變量的類型。這很容易理解,因爲 auto 是根據變量的初始值來推導出變量類型的,如果不初始化,變量的類型也就無法推導了。

auto 將變量的類型和初始值綁定在一起,而 decltype 將變量的類型和初始值分開;雖然 auto 的書寫更加簡潔,但 decltype 的使用更加靈活。

請看下面的例子:





auto n1 = 10;decltype(10) n2 = 99;

auto url1 = "http://c.biancheng.net/cplus/";decltype(url1) url2 = "http://c.biancheng.net/java/";

auto f1 = 2.5;decltype(n1*6.7) f2;

這些用法在前面的兩節中已經進行了分析,此處就不再贅述了。

對 cv 限定符的處理

「cv 限定符」是 const 和 volatile 關鍵字的統稱:

  • const 關鍵字用來表示數據是隻讀的,也就是不能被修改;

  • volatile 和 const 是相反的,它用來表示數據是可變的、易變的,目的是不讓 CPU 將數據緩存到寄存器,而是從原始的內存中讀取。


在推導變量類型時,auto 和 decltype 對 cv 限制符的處理是不一樣的。decltype 會保留 cv 限定符,而 auto 有可能會去掉 cv 限定符。

以下是 auto 關鍵字對 cv 限定符的推導規則:



  • 如果表達式的類型不是指針或者引用,auto 會把 cv 限定符直接拋棄,推導成 non-const 或者 non-volatile 類型。

  • 如果表達式的類型是指針或者引用,auto 將保留 cv 限定符。


下面的例子演示了對 const 限定符的推導:


//非指針非引用類型const int n1 = 0;

auto n2 = 10;n2 = 99;  //賦值不報錯

decltype(n1) n3 = 20;n3 = 5;  //賦值報錯

//指針類型const int *p1 = &n1;

auto p2 = p1;*p2 = 66;  //賦值報錯

decltype(p1) p3 = p1;*p3 = 19;  //賦值報錯

在 C++ 中無法將一個變量的完整類型輸出,我們通過對變量賦值來判斷它是否被 const 修飾;如果被 const 修飾那麼賦值失敗,如果不被 const 修飾那麼賦值成功。雖然這種方案不太直觀,但也是能達到目的的。

n2 賦值成功,說明不帶 const,也就是 const 被 auto 拋棄了,這驗證了 auto 的第一條推導規則。p2 賦值失敗,說明是帶 const 的,也就是 const 沒有被 auto 拋棄,這驗證了 auto 的第二條推導規則。

n3 和 p3 都賦值失敗,說明 decltype 不會去掉表達式的 const 屬性。




對引用的處理

當表達式的類型爲引用時,auto 和 decltype 的推導規則也不一樣;decltype 會保留引用類型,而 auto 會拋棄引用類型,直接推導出它的原始類型。請看下面的例子:

#include <iostream>using namespace std;int main() {    int n = 10;    int &r1 = n;    //auto推導    auto r2 = r1;    r2 = 20;    cout << n << ", " << r1 << ", " << r2 << endl;    //decltype推導    decltype(r1) r3 = n;    r3 = 99;    cout << n << ", " << r1 << ", " << r3 << endl;    return 0;}

運行結果:

10, 10, 20
99, 99, 99

從運行結果可以發現,給 r2 賦值並沒有改變 n 的值,這說明 r2 沒有指向 n,而是自立門戶,單獨擁有了一塊內存,這就證明 r 不再是引用類型,它的引用類型被 auto 拋棄了。

給 r3 賦值,n 的值也跟着改變了,這說明 r3 仍然指向 n,它的引用類型被 decltype 保留了。




總結

auto 雖然在書寫格式上比 decltype 簡單,但是它的推導規則複雜,有時候會改變表達式的原始類型;而 decltype 比較純粹,它一般會堅持保留原始表達式的任何類型,讓推導的結果更加原汁原味。

從代碼是否健壯的角度考慮,我推薦使用 decltype,它沒有那麼多是非;但是 decltype 總是顯得比較麻煩,尤其是當表達式比較複雜時,例如:

vector<int> nums;
decltype(nums.begin()) it = nums.begin();

而如果使用 auto 就會清爽很多:

vector<int> nums;
auto it = nums.begin();

在實際開發中人們仍然喜歡使用 auto 關鍵字(我也這麼幹),因爲它用起來簡單直觀,更符合人們的審美。如果你的表達式類型不復雜,我還是推薦使用 auto 關鍵字,優雅的代碼總是叫人賞心悅目,沉浸其中。

圖片


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