【C++ grammar】Enhancement for Type System (C++11 對型別系統的增強)

拾牙慧者發表於2020-10-03

資料型別 (Data type)

int, long int, double, struct, char *, float [], int (*f)()…

計算機程式構造塊

計算機程式構造塊是不同大小粒度的計算機程式組成部分,它包括變數、表示式、函式或者模組等。

型別系統 (Type System)

型別系統:在程式語言中,“型別系統”是將“type”屬性指定給不同計算機程式構造塊的規則集
為什麼使用型別系統:減少程式中可能出現的bug
1、定義不同程式塊之間的介面
2、檢查多個塊之間是否以一致的方式連線在一起
型別系統分為:靜態型別和動態型別
型別系統存在於我們編寫程式中,編譯器會幫我們檢查型別系統。
如果檢查發生在編譯期(編譯的時候檢查型別系統是否一致),稱為靜態型別
如果檢查發生在執行期(執行的時候檢查型別系統是否一致),稱為動態型別
如果檢查同時存在與編譯期間和執行期間,稱為混合型別
C、C++、Java是靜態型別
Python是動態型別
在這裡插入圖片描述

Automatic Type Deduction: auto (C++11) 自動型別推導:auto關鍵字

C++03及之前的標準種,auto放在變數宣告之前,宣告變數的儲存策略。但是這個關鍵字常省略不寫。
C++11中,auto關鍵字放在變數之前,作用是在宣告變數的時候根據變數初始值的型別自動為此變數選擇匹配的型別
例:

int a = 10;
auto au_a = a;//自動型別推斷,au_a為int型別 
cout << typeid(au_a).name() << endl;

result:

int

注意點:
1、auto 變數必須在定義時初始化,這類似於const關鍵字

auto a1 = 10;  //正確 
auto b1;   //錯誤,編譯器無法推導b1的型別
b1 = 10;

2、定義在一個auto序列的變數必須始終推導成同一型別

auto a4 = 10, a5{20};   //正確 
auto b4{10}, b5 = 20.0; //錯誤,沒有推導為同一型別

3、如果初始化表示式是引用或const,則去除引用或const語義。

int a{10}; int &b = a;
auto c = b;   //c的型別為int而非int&(去除引用)
const int a1{10};
auto b1 = a1; //b1的型別為int而非const int(去除const) 

4、如果auto關鍵字帶上&號,則不去除引用或const語意

int a = 10; int& b = a;
auto& d = b;//此時d的型別才為int&
const int a2 = 10;
auto& b2 = a2;//因為auto帶上&,故不去除const,b2型別為const in

5、初始化表示式為陣列時,auto關鍵字推導型別為指標

int a3[3] = { 1, 2, 3 };
auto b3 = a3;
cout << typeid(b3).name() << endl; //輸出int * (輸出與編譯器有關)

6、若表示式為陣列且auto帶上&,則推導型別為陣列型別

int a7[3] = { 1, 2, 3 };
auto& b7 = a7;
cout << typeid(b7).name() << endl; //輸出int [3] (輸出與編譯器有關)

7、C++14中,auto可以作為函式的返回值型別和引數型別

auto max(int x , int y)
{
	return x>y?x:y;
}

我們在使用auto時有時會遇到一些特殊情況。

1、 “int x = 3;” 能變成auto形式嗎?
當我們非常希望能夠在變數定義的時候,【明確】地指出變數的型別,而且不希望隨便更改其型別,那麼我們可以使用下面的方法:

auto x = int {3};  // 初始化列表

auto y = int {3.0}; // 編譯器報錯,初始化列表不能窄化

auto z = int (3.0);  // C風格的強制型別轉換,z的值是整數3

2、 auto和初始化列表一起用 要避免在一行中使用直接列表初始化和拷貝列表初始化,也就是,下面的程式碼是有問題的:

auto x { 1 }, y = { 2 };  // 不要同時使用直接和拷貝列表初始化

在這裡插入圖片描述
“AAA原則”:總是使用auto!
問題:能用auto宣告C風格的陣列嗎?
如果你嘗試讓C++11編譯器編譯如下程式碼,它會報錯

// 包含標頭檔案
// 宣告主函式
// ......
auto x[] = {1,2,3};

auto 不能用於宣告陣列,否則無法通過編譯,報auto型別不能出現在頂級陣列型別中。
也就是說auto只能是指標指向陣列的而不能去宣告一個陣列。

自動型別推導:decltype關鍵字

1、關鍵字decltype的用法
decltype利用已知型別宣告新變數。
有了auto,為什麼還要整出一個decltype?原因是,我們有時候想要從表示式的型別推斷出要定義的變數型別,但不想用該表示式的值初始化變數。
decltype是在編譯期推導一個表示式的型別,而不用初始化,其語法格式有點像sizeof。它只做靜態分析,因此它不會導致已知型別表示式執行。
decltype 主要用於泛型程式設計(模板)
2、程式碼示例

#include<iostream>
using namespace std;
int  fun1()  { return 10;  }
auto fun2()  { return 'g'; }  // C++14
int main(){
    // Data type of x is same as return type of fun1()
    // and type of y is same as return type of fun2()
    decltype(fun1()) x;  // 不會執行fun1()函式,相當於 int x;
    decltype(fun2()) y = fun2();		//auto y=>char y;
    cout << typeid(x).name() << endl;
    cout << typeid(y).name() << endl;
    return 0;
}

我們可以發現,我們將fun2的‘g’改變,例如改成4,y型別也改了。
這樣我們修改了返回值型別,程式仍然能夠正常執行。
這說明,使用型別推導是有好處的。
3、decltype與auto的對比
decltype和auto都是C++11自動型別推導的關鍵字。它們有很多差別:
auto忽略最上層的const,decltype則保留最上層的const
auto忽略原有型別的引用,decltype則保留原有型別的引用
對解引用操作,auto推斷出原有型別,decltype推斷出引用

4、auto推斷時會實際執行,decltype不會執行,只做分析。
總之在使用中過程中和const、引用和指標結合時需要特別小心。

小結

C++11對C++03的增強,或者說C++11的“Modern”的特點之一就是由auto和decltype構成的自動型別推導系統。

但是,auto與初始化列表結合,又有一些坑。你在寫程式碼時如果經常將auto與列表初始化一起使用,那麼會遇到一些問題。本節只介紹auto的常見用法。auto與初始化列表結合的坑,得由你自己去踩了。

為了說明auto有多麼複雜,這裡摘取 https://cppreference.com 網站列出的auto的語法格式:
在這裡插入圖片描述

相關文章