文章目錄
- 類型別名
- 概念
- 關鍵字 typedef
- 別名聲明 (alias declaration) using
- 指針、常量和類型別名
- 類型別名簡化多維數組指針
- auto類型說明符
- 概念
- 復合類型、常量和auto
- decltype類型指示符
- 概念
- decltype和引用
類型別名
概念
有兩種方法可用于定義類型別名。
關鍵字 typedef
typedef double wages; // wages是double的同義詞
typedef wages base, *p; // base是double的同義詞,p是double*的同義詞
關鍵字typedef作為聲明語句中的基本數據類型的一部分出現。含有typedef的聲明語句定義的不再是變量而是類型別名。這和以前的聲明語句一樣,這里的聲明符也可以包含類型修飾,從而也能由基本數據類型構造出復合類型來。
別名聲明 (alias declaration) using
using SI = Sales_item; // SI是Sale_item的同義詞
這種方法用關鍵字using作為別名聲明的開始,其后緊跟別名和等號,作用是把等號左側的名字規定成等號右側類型的別名。
類型別名和類型的名字等價,只要是類型的名字能出現的地方,就能使用類型別名:
wages hourly, weekly; // 等價于double hourly, weekly;
SI item; // 等價于Sales_item item
指針、常量和類型別名
如果某個類型別名指代的是復合類型或常量,那么把它用到聲明語句里就會產生意想不到的后果。
typedef char *pstring;
const pstring cstr = 0; // cstr是指向char的常量指針
const pstring *ps; // ps是一個指針,它的對象是指向char的常量指針
上述兩條聲明語句的基本類型都是const pstring,const是對給定類型的修飾。pstring實際上是指向char的指針,const pstring則是指向char的常量指針,而非指向常量字符的指針。
也就是說,遇到一條使用了類型別名的聲明語句時,把類型別名直接替換成它本來的樣子是錯誤的:
const char *cstr = 0; // 是對const pstring cstr的錯誤理解
聲明語句中用到pstring時,其基本數據類型是指針。使用char*重寫了聲明語句后,數據類型就變成了char,*成為了聲明符的一部分。上述改寫的結果是,const char成了基本數據類型。前后兩種聲明含義截然不同,前者聲明了一個指向char的常量指針,改寫后的形式則聲明了一個指向const char的指針。
類型別名簡化多維數組指針
typedef int int_array[4];
int main()
{int ia[3][4] = {};for (int_array *p = ia; p != ia+3; ++p) {for(int *q = *p; q != *p + 4; ++q){std::cout << *q << ' ';}cout << endl;}
}
具體對這個語句來說,別名就是:int_array。[4]不屬于別名的一部分,而表示一種已有的數據類型,即:將類型“4個整數組成的數組”取一個別名為int_array。
怎么理解呢?首先,
int ia[4];
是常見的定義格式。再在其前面添加關鍵字 typedef,變成
typedef int ia[4];
最后將數組名ia改為自己想要的一個別名int_array即可。
注意:原本的ia本意是數組名,屬于變量范疇,而int_array則是新數據類型名(即別名),類型為int[4],本質不一樣了哦。
auto類型說明符
概念
C++11新標準引入了auto類型說明符,能讓編譯器替我們去分析表達式所屬的類型。和原來那些只對應一種特定類型的說明符(int、double等)不同,auto讓編譯器通過初始值來推算變量的類型。auto定義的變量必須有初始值:
// 由val1和val2相加的結果可以推斷出item的類型
auto item = val1 + val2;
val1和val2兩個變量的類型如果是double,則item也是double,如果兩個變量是int,則item也是int,以此類推。
當然也允許在一條語句中聲明多個變量。因為一條聲明語句只能有一個基本數據類型,所以該語句中所有變量的初始基本數據類型必須都一樣:
auto i = 0, *p = &i; // 正確:i是整數、p是整型指針
auto sz = 0, pi = 3.14; // 錯誤:sz和pi的類型不一樣
復合類型、常量和auto
編譯器推斷出來的auto類型有時候和初始值的類型并不完全一樣,編譯器會適當地改變結果類型使其更符合初始化規則。
- 當引用被用作初始值時,編譯器以引用對象地類型作為auto的類型:
int i = 0, &r = i;
auto a = r; // a是一個整數(因為i是一個整數)
- auto一般會忽略掉頂層const,同時底層const則會保留下來:
const int ci = i, &cr = ci;
auto b = ci; // b是一個整數(ci的頂層const特性被忽略)
auto c = cr; // c是一個整數(同上)
auto d = &i; // d是一個整型指針(整數的地址就是指向整數的指針)
auto e = &ci; // e是一個指向整數常量的指針(對常量對象取地址是一種底層const)
- 如果希望推斷出的auto類型是一個頂層const,需要明確指出:
const auto f = ci; // ci的推演類型是int,f 是const int
- 還可以將引用的類型設為auto,此時原來的初始化規則仍然適用:
auto &g = ci; // g是一個整型常量引用,綁定到ci
auto &h = 42; // 錯誤:不能為非常量引用綁定字面值
const auto &j = 42; // 正確:可以為常量引用綁定字面值
設置一個類型為auto的引用時,初始中的頂層常量屬性仍然保留,也就是常量對象依然必須由常量引用綁定。 如果給初始值綁定一個普通引用,此時的常量就不是頂層常量了(由于引用不是對象,因此對于引用而言變成了底層const,依然是會忽略頂層const的毛病)。
- 要在一條語句中定義多個變量,切記,符號&和*只從屬于某個聲明符,而非基本數據類型的一部分,因此初始值必須是同一種類型:
auto k = ci, &l = i; // k是整數,l是整型引用
auto &m = ci, *p = &ci; // m是對整型常量的引用,p是指向整型常量的指針
auto &n = i, *p2 = &ci; // 錯誤:i的類型是int而&ci的類型是const int
- auto可以與指針和引用結合
int main()
{int i = 4;auto a1 = &i;auto *a2 = &i;auto& a3 = i;cout <<"a1的類型為:" << typeid(a1).name() << endl;cout << "a2的類型為:" << typeid(a2).name() << endl;cout << "a3的類型為:" << typeid(a3).name() << endl;
}
因為auto可以識別指針類型,所以加不加*都可以,但是引用必須加上&,因為C++沒有引用這個類型,引用只是一個修飾別名,所以它的本質還是int。
- auto不能作為函數的參數
auto不能作為形參的類型,編譯器無法對i和j的類型進行推導。
8.auto不能直接用來聲明數組
decltype類型指示符
概念
有時會希望從表達式的類型推斷出要定義的變量的類型,但是不想用該表達式的值初始化變量,因此也就不能用auto類型說明符。為了滿足這一要求,新標準引入了第二種類型說明符decltype,它的作用是選擇并返回操作數的數據類型。在此過程中,編譯器分析表達式并得到它的類型,卻不實際計算表達式的值:
decltype(f()) sum = x; // sum的類型就是函數f的返回類型
編譯器并不實際調用函數f,而是使用當調用發生時 f 的返回值類型作為sum的類型。換句話說,編譯器為sum指定的類型是假如 f 被調用的話將會返回的那個類型。
decltype處理頂層const和引用的方式與 auto 有些許不同。如果decltype使用的表達式是一個變量,則decltype返回該變量的類型(包括頂層const和引用在內):
const int ci = 0, &cj = ci;
decltype(ci) x = 0; // x的類型是const int
decltype(cj) y = x; // y的類型是const int&,y綁定到變量x
decltype(cj) z; // 錯誤:z是一個引用,必須初始化
因為cj是一個引用,decltype(cj)的結果就是引用類型,因此作為引用的z必須被初始化。
引用從來都是作為其所指對象的同義詞出現,只有在decltype處是一個例外。
decltype和引用
如果decltype使用的表達式不是一個變量,則decltype返回表達式結果對應的類型。有些表達式將向decltype返回一個引用類型。當這種情況發生時,意味著該表達式的結果對象能作為一條賦值語句的左值:
int i = 42, *p =&i, &r = i;
decltype(r+0) b; // 正確:加法的結果是int,因此b是一個(未初始化的)int
decltype(*p) c; // 錯誤:c是int&,必須初始化
因為r是一個引用,因此decltype?的結果是引用類型。如果想讓結果類型是 r 所指的類型,可以把 r 作為表達式的一部分,如 r+0,顯然這個表達式的結果將是一個具體值而非一個引用。
另一方面,如果表達式的內容是解引用操作,則decltype將得到引用類型。解引用指針可以得到指針所指的對象,而且還能給這個對象賦值。因此,decltype(*p)的結果就是int&,而非int。
decltype和auto的另一處重要區別是,decltype的結果類型與表達式形式密切相關。 對于decltype所用的表達式來說,如果變量名加上了一對括號,則得到的類型與不加括號時會有不同。如果declytpe使用的是一個不加括號的變量,則得到的結果就是該變量的類型;如果給變量加上一層或多層括號,編譯器就會把它當成一個表達式。 變量是一種可以作為賦值語句左值的特殊表達式,所以這樣的decltype就會得到引用類型:
// decltype的表達式如果是加上了括號的變量,結果將是引用
decltype((i)) d; // 錯誤:d是int&,必須初始化
decltype(i) e; // 正確:e是一個(未初始化的)int
declytpe((variable))的結果永遠是引用,而declytpe(variable)結果只有當variable本身就是一個引用時才是引用。