C++ 處理型別名(typedef,auto和decltype)

2022-07-26 12:00:50

隨著程式越來越複雜,程式中用到的型別也越來越複雜,這種複雜性體現在兩個方面。一是一些型別難於「拼寫」,它們的名字既難記又容易寫錯,還無法明確體現其真實目的和含義。二是有時候根本搞不清到底需要的型別是什麼,程式設計師不得不回過頭去從程式的上下文中尋求幫助。

typedef

typedef來源於 C,可以給型別使用別名:

typedef int size; // size 就代表int
typedef double wage, *p; // wage代表double,而p代表double*
typedef double adouble[10]; //adouble代表double[10]

第一個很簡單,後面的麼看?把typedef去掉看後面的宣告,後面的識別符號型別是什麼,那麼代表什麼,宣告可以千奇百怪,typedef自然也千奇百怪。在新標準中,可以用using來替代它:

using p = double*;

有一點要注意:用了這個東西,識別符號就是一個整體了,不能像define一樣做字串替換然後又拆開。比如:

typedef char * charp;
const charp cp = 0;

charp是一個整體,被const修飾。所以它實際是「常數指標」。要是拆開變成const char *的話,就成為了「指向常數的指標」,這顯然不對。

auto

程式設計時常常需要把表示式的值賦給變數,這就要求在宣告變數的時候清楚地知道表示式的型別。然而要做到這一點並非那麼容易,有時甚至根本做不到。為了解決這個問題,C++11新標準引入了auto型別說明符,用它就能讓編譯器替我們去分析表示式所屬的型別。和原來那些只對應一種特定型別的說明符(比如 double)不同,auto 讓編譯器通過初始值來推算變數的型別。顯然,auto定義的變數必須有初始值:

auto i = 3 + 2; //從右推左, auto填充為int
auto m = 9。0, *p = &m; // auto填充為double
auto a = 3, b = 2.0; // 不可以,只能填充一個值

vector<int> v{1, 2, 3, 4};
auto it = v.begin(); // 型別為vector<int>::iterator,避免冗長的宣告。

auto是從右邊型別推左邊型別,並不是變數宣告時的型別。

int i = 0, &r = i;
auto a = r; //a是int,不是int&
auto &b = r; //參照需顯式指定

const int c = 0;
auto x = c; // 填充為int,右const被忽略
const auto y = c; // 右const要手動指定
auto p = &c; // 填充為const int *,左const會被保留

int ir[10];
auto x = ir; // 陣列名被轉化為指標,值int*
auto &x = ir; // 陣列名還是陣列,值為int[10]

auto也有助於提升程式的可拓展性,每個變數的型別都依賴其他變數,只需第一個宣告的變數,後面所有變數的型別同時也就更改了,很方便。

auto a = 3;
for (auto i(a); i < 12; i++)
	...

a = 3改為a = 3ll,則所有變數都被擴充套件為64位元型別。

auto將陣列轉為指標大不相同

受譚浩強影響,很多人覺得「陣列就是指標」。這種說法是錯誤的。陣列就是陣列啦,只是某些場合可以轉化成元素頭指標。C++ Primer裡提出一個有趣的例子:

int a[3][3] = {};
for (auto &row: a)
//for (auto row: a)
for (auto &col: row) cout << col << endl;

上面的程式碼可以正常執行,但是換成註釋語句後就不行了,原因在前面的範例程式碼裡已經提過了。auto &row: a裡,row的型別是int[3],是可迭代的。但auto row: a裡,它的型別成為了int*,不可迭代,自然第二個for執行不了。陣列到指標是一種退化,因為只有頭地址而丟失了長度。

auto用於函數宣告

在更新的版本中,auto還可以用於形參型別和返回值:

auto triple_adder(auto &&a, auto &&b, auto &&c) {
	return a + b + c;
}
main() {
    cout << triple_adder(1, 2, 3.2) << endl; //6.2
    cout << triple_adder(string("he"), string("ll"), string("oo")) << endl; //helloo
}

將引數宣告為auto,作用與模板完全相同(但更簡潔),會根據型別生成多個函數。而返回值設為 auto,就可以根據return語句的型別自動生成返回型別。不過auto只是代表「自動推斷一種型別」。C++的函數返回值只能有一種型別。如果多處return型別不一致,推斷失敗就會報錯。

decltype

decltype是和 auto 一起推出的,不過沒那麼簡潔,用的不多。decltyle是完整嚴格的型別推斷。

decltype(3) i = 12; // 3的型別是int,所以是int
decltype(f()) j = 0; // f的返回型別決定j的型別,但是並不會執行f

auto不同,decltype的規則為「括號裡面是什麼就是什麼」:

const int a = 3;
decltype(a) ca = a; //ca 是const int

int b = 5, &rb = b;
decltype(b) rb = a; //rb 是int&
decltype(b+0) rrb = 1; //是int

int ar[10];
decltype(ar) p; //是int[10]