1. 程式人生 > >C++11強型別列舉

C++11強型別列舉

1.傳統列舉型別的缺陷

列舉型別是C/C++中使用者自定義的構造型別,它是由使用者定義的若干列舉常量的集合。列舉值對應整型數值,預設從0開始。比如定義一個描述性別的列舉型別。

enum Gender{Male,Female};

其中列舉值Male被編譯器預設賦值為0,Female賦值為1。傳統列舉型別在設計上會存在以下幾個問題。

(1)同作用域同名列舉值會報重定義錯誤。傳統C++中列舉常量被暴漏在同一層作用域中,如果同一作用域下有兩個不同的列舉型別,但含有同名的列舉常量也是會報編譯錯誤的,比如:

enum Fruits{Apple,Tomato,Orange};
enum Vegetables{Cucumber,Tomato,Pepper};	//編譯報Tomato重定義錯誤

其中水果和蔬菜兩個列舉型別中包含同名的Tomato列舉常量會導致編譯錯誤。因為enum則是非強作用域型別,列舉常量可以直接訪問,這種訪問方式與C++中具名的namespace、class/struct以及union必須通過"名字::成員名"的訪問方式大相徑庭。

(2)由於列舉型別被設計為常量數值的“別名”,所以列舉常量總是可以被隱式轉換為整型,且使用者無法為列舉常量定義型別。

(3)列舉常量佔用儲存空間以及符號性不確定。C++標準規定C++列舉所基於的“基礎型別”是由編譯器來具體實現,這會導致列舉型別成員的基本型別存在不確定性問題,尤其是符號性問題,即。考察如下示例:

enum A{A1=1,A2=2,ABig=0xFFFFFFFFU};
enum B{B1=1,B2=2,BBig=0xFFFFFFFFFUL};

int main()
{
	cout<<sizeof(A1)<<endl;	//4
	cout<<ABig<<endl;		//4294967295
	cout<<sizeof(B1)<<endl;	//8
	cout<<BBig<<endl;		//68719476735
}

以上輸出結果是在Linux平臺下使用g++編譯輸出的結果,在VC++(VS2017)中的輸出結果分別是4、-1、4和-1。可見不同編譯器對列舉常量的整型型別的寬度和符號有著不同的實現。GNU C++會根據列舉數值的型別使用不同寬度和符號的整型,VC++則始終以有符號int來表示列舉常量。

為了解決以上傳統列舉型別的缺陷,C++11引入了強型別列舉解決了這些問題。

2.強型別列舉

非強作用域型別,允許隱式轉換為整型,列舉常量佔用儲存空間以及符號性的不確定,都是列舉類缺點。針對這些缺點,C++11引入了一種新的列舉型別——強型別列舉(strong-typed enum)。

強型別列舉使用enum class語法來宣告:

enum class Enumeration{VAL1,VAL2,VAL3=100,VAL4};

強型別列舉具有如下幾個優點:
(1)強作用域,強型別列舉成員的名稱不會被輸出到其父作用域,所以不同列舉型別定義同名列舉成員編譯不會報重定義錯誤。進而使用列舉型別的列舉成員時,必須指明所屬範圍,比如Enum::VAL1,而單獨的VAL1則不再具有意義;
(2)轉換限制,強型別列舉成員的值不可以與整型發生隱式相互轉換。比如比如Enumeration::VAL4==10;會觸發編譯錯誤;
(3)可以指定底層型別。強型別列舉預設的底層型別是int,但也可以顯示地指定底層型別。具體方法是在列舉名稱後面加上":type",其中type可以是除wchar_t以外的任何整型。比如:

enum class Type:char{Low,Middle,High};

注意:
(1)宣告強型別列舉的時候,既可以使用關鍵字enum class,也可以使用enum struct。事實上,enum struct與enum class在語法上沒有任何區別。
(2)由於強型別列舉是強型別作用域的,故匿名的enum class可能什麼都做不了,如下程式碼會報編譯錯誤:

enum class{General,Light,Medium,Heavy}weapon;
int main()
{
	weapon=Medium;						//編譯出錯
	bool b=weapon == weapon::Medium;	//編譯出錯
	return 0;
}

當然對於匿名強型別列舉我們還是可以使用decltype來獲得其型別並進而使用,但是這樣做可能違背強型別列舉進行匿名的初衷。

3.C++11對傳統列舉型別的擴充套件

傳統列舉型別為了配合C++11引入的強型別列舉,C++11對傳統列舉型別進行了擴充套件。
(1)底層的基本型別可以在列舉名稱後加上":type",其中type可以是除wchar_t以外的任何整型,比如:

enum Type:char{Low,Middle,High};

(2)C++11中,列舉型別的成員可以在列舉型別的作用域內有效。比如:

enum Type{Low, Middle, High };
Type type1 = Middle;
Type type2 = Type::Middle;

其中Middle與Type::Middle都是合法的使用形式。


參考文獻

[1]深入理解C++11[M].5.1強型別列舉.P155-P161
[1]C++11強型別列舉——列舉類