C++ 11 新特性之型別推斷與型別獲取
這是C++11新特性介紹的第二部分,涉及到C++11這次更新中較為重要的特性型別推斷(auto)與型別獲取(decltype)。
不想看toy code的讀者可以直接拉到文章最後看這部分的總結。
簡單的型別推斷
C++11新標準中增加了auto型別說明符,可以讓編譯器幫我們分析表示式的型別。
double val1 = 1.1, val2 = 2.2; auto sum = val1 + val2; auto val3 = 0.3, *p = &val3; //auto val4 = 0, val5 = 0.0; // wrong. different types. //auto sum2; // wrong, auto type must be initialized. double val6 = 1.6, &rval6 = val6; auto aval6 = rval6; aval6 = 6.0; // aval6 is not a reference. cout<<"test simple auto:\n"<<val1<<'\t'<<val2<<'\t'<<sum<<'\t'<<val3<<'\t'<<p<<'\t'<<val6<<'\t'<<rval6<<'\t'<<aval6<<endl;
需要注意的是
1.使用auto定義的變數必須有初始值,不然無法進行型別推斷
2.在同一條語句中使用auto定義的變數,其基礎型別必須一致
const和auto
auto在進行型別推斷時,一般會忽略頂層const(top-level const),而保留底層const(low-level const)。如果想要保留頂層const,則必須顯式的在auto前新增const指示符。
所謂頂層const,指的是當前的資料型別本身是常量,如double,int或者相關的指標本身是常量;
而底層const,指的是如指標、引用等複合型別,其所指向的資料型別是常量。
const int val7 = 1, &rval7 = val7; auto aval7 = val7; // remove top-level const aval7 = 7; auto aval8 = rval7; // remove top-level const aval8 = 8; auto aval9 = &val6; // not const *aval9 = 9; auto aval10 = &val7; // keep low-level const //*aval10 = 10; // wrong. const int can't be changed. const auto aval11 = val7; // top-level const auto //aval11 = 11; // wrong. const int can't be changed. auto &aval12 = val7; // keep top-level const //aval12 = 12; // wrong. const reference auto &aval13 = val6; aval13 = 13.0; //auto &aval14 = 42; // wrong. must be const auto const auto &aval15 = 15; //aval15 = 16; // wrong. const reference. //auto &aval16 = aval7, *aval17 = &val7; // wrong. type not consistent cout<<"test auto and const:\n"<<val7<<'\t'<<rval7<<'\t'<<aval7<<'\t'<<aval8<<'\t'<<*aval9<<'\t'<<aval10<<'\t'<<aval11<<'\t'<<aval12<<'\t'<<aval13<<'\t'<<aval15<<endl;
當定義一個auto的引用時,頂層const被保留,如上述測試程式碼中的aval12所示。另外,輸出的結果中,有些數值可能和預想的不太一樣,可以思考一下是為什麼^_^(Tips: 和引用有關)。
型別獲取decltype
decltype(expr)可以獲得expr表示式對應的型別,並且不會對expr具體求值。
int d() { cout<<"This function shouldn't be called."<<endl; return 17; } decltype(d()) dval17 = 15.2; cout<<"test decltype:\n"<<dval17<<endl;
decltype與const
decltype處理const的方式與auto不同。
1.如果decltype中的表示式是一個變數,那麼返回該變數的型別(包括頂層const)
2.如果decltype中的表示式不是變數,則返回該表示式結果對應的型別。
看上去沒啥區別?其實這裡的規則導致了decltype(r+0) decltype((i))這種詭異的寫法。還是具體看程式碼吧。
decltype(val7) val18 = 0; decltype(rval7) val19 = val18; //val19 = 10; // wrong. val19 is a reference to const int. cout<<"test decltype and const:\n"<<val18<<'\t'<<val19<<endl; //double *pval20 = &val6; //decltype(*pval20) val21; // wrong. decltype(*pval20) = double&, must be initialized. decltype(rval6 + 0) val22; //decltype((val6)) val23; //wrong. decltype((val6)) == double&, must be initialized. decltype(val6) val24; cout<<"test decltype and reference:\n"<<val22<<'\t'<<val24<<endl;
上面程式碼中需要注意的地方有:
1.val21處,如果decltype中的表示式是一個解引用操作,那麼將得到一個引用型別,所以必須初始化。
2.val22處,rval6是一個引用型別(double&),如果我們需要獲得這個引用的基礎型別(即double),那麼使用rval6 + 0這樣一個表示式,顯然這個表示式的結果將不是引用了。
3.val23和val24處,如果decltype中的變數加上了括號,那麼就會被當作表示式處理;而變數是一種可以作為左值被賦值的特殊表示式,因此decltype對於這種帶括號的變數(val23處),就會得到一個引用型別。
使用auto縮寫型別
string name = "Yubo"; auto length = name.size(); cout<<"test auto with complex type:\n"<<length<<endl;
不用費勁寫string::size_type了^o^
使用auto簡化宣告
宣告指向陣列的指標總是一件令人痛苦的事情:
int val25[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; cout<<"test auto to simplify type:\n"; cout<<"old way:\n"; for(int (*p)[4] = val25; p != val25 + 3; p++) { for(int *q = *p; q != *p + 4; q++) { cout<<*q<<'\t'; } cout<<'\n'; }
有了auto之後,我們可以像下面這樣清爽:
cout<<"new way:\n"; for(auto ap = val25; ap != val25 + 3; ap++) { for(auto aq = *ap; aq != *ap + 4; aq++) { cout<<*aq<<'\t'; } cout<<'\n'; }
使用decltype簡化函式返回型別
如果我們已經知道某個函式會返回什麼物件,然而這個物件又是一個型別複雜不好寫的物件,那麼decltype就可以派上用場了。
int odd[] = {1, 3, 5, 7, 9}; int even[] = {0, 2, 4, 6, 8}; decltype(odd) *get_odd_or_even(int i) { return (i % 2) ? &odd : &even; } auto val26 = get_odd_or_even(1); cout<<"test decltype to simplify func return type:\n"; for(auto p = begin(*val26); p != end(*val26); p++) { cout<<p<<' '<<*p<<'\t'; } cout<<endl;
使用auto動態分配記憶體
auto可以和new配合,來動態分配記憶體,並進行初始化。
auto val27 = new auto(val24); auto val28 = new auto(name); cout<<"test auto to new object with a given obj:\n"; cout<<*val27<<'\t'<<*val28<<'\t'<<val28<<'\t'<<&name<<endl; auto val29 = new auto(odd); // right. can use auto to new a pointer to an array for(auto p = *val29; p != *val29 + 5; p++) { cout<<p<<' '<<*p<<'\t'; } cout<<endl; //auto val30 = new auto[10](val24); // wrong. can't use auto to new an array int *val31 = new int[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // this is right cout<<val31<<'\t'<<val31[0]<<"to"<<val31[9]<<endl;
這裡有幾處需要留意的地方:
1.val29處,只是new出了一個指向陣列的指標,並沒有複製陣列的值。因此在下面迴圈中列印出的p值(地址)和odd陣列的地址是一樣的。
2.不可以使用auto來分配一個動態陣列。這是因為使用new分配陣列時,不支援圓括號的初始化方式,只支援花括號的列表初始化方式。
總結
- 可以使用auto說明符,讓編譯器幫我們推斷型別。
- auto在進行型別推斷時,一般會忽略頂層const(top-level const),而保留底層const(low-level const)。
- decltype(expr)可以獲得expr表示式對應的型別,並且不會對expr具體求值。
- decltype(rval + 0)可以獲得值型別(非引用),decltype(*p)獲得引用型別,decltype((val))獲得引用型別。
- 使用auto可以縮寫一些複雜難寫的型別。
- 使用auto可以簡化型別宣告,尤其是陣列和指標混合的宣告。
- 在知道某一函式會返回什麼物件時,可以使用decltype可以簡化函式返回型別。
- auto和new可以配合以動態分配記憶體,但是不能用於動態分配陣列。
完整程式碼詳見 auto_decltype.cpp
相關文章
- Java 10新特性:型別推斷Java型別
- c/c++ 模板 型別推斷C++型別
- TypeScript 型別推斷TypeScript型別
- C++ auto 型別推斷注意的地方C++型別
- Java™ 教程(型別推斷)Java型別
- C++型別推導C++型別
- Objective-C型別推斷Object型別
- c++任意變數型別獲取相關C++變數型別
- C# 9.0 新特性之目標型別推導 new 表示式C#型別
- Java8 新語法習慣 (型別推斷)Java型別
- Java反射獲取位元組碼以及判斷型別Java反射型別
- JavaScript 資料型別與型別判斷詳解JavaScript資料型別
- C/C++獲取變數型別並輸出C++變數型別
- Postgres 9.2 新特性之:範圍型別 (Range Types)型別
- PHP 獲取裝置型別PHP型別
- Oracle11g新特性——LOB型別功能增強Oracle型別
- lodash原始碼分析之獲取資料型別原始碼資料型別
- typeScript 型別斷言、聯合型別和交叉型別(七)TypeScript型別
- Typescript型別推斷技巧你知道麼?TypeScript型別
- 【C++】C++之型別轉換C++型別
- 獲取作業系統型別作業系統型別
- JavaScript獲取物件資料型別JavaScript物件資料型別
- 獲取 iOS 裝置的型別iOS型別
- TS資料型別:型別別名/聯合型別/字面量型別/型別推論等綱要資料型別
- 型別斷言型別
- Python 3 新特性:型別註解Python型別
- 【Java】Java新特性--Records記錄型別Java型別
- PHP 資料型別之檢視和判斷資料型別PHP資料型別
- 值型別與引用型別型別
- C++11獲取double型別的最大最小值C++型別
- .net framework新特性之隱式型別化的變數Framework型別變數
- C#的型別——值型別與引用型別C#型別
- Flutter獲取當前網路型別Flutter型別
- .net core 獲取檔案MIME型別型別
- C#特性-匿名型別與隱式型別區域性變數C#型別變數
- python之 資料型別判定與型別轉換Python資料型別
- python 中如何判斷獲取檢視變數的型別Python變數型別
- ECMAScript 原始型別與引用型別型別