通過《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 關鍵字,優雅的代碼總是叫人賞心悅目,沉浸其中。