「Scoped and strongly typed enums」是 C++11 時所引進的一個新的功能,主要是要取代舊的列舉型別(enum)。
他的基本用法,是在 enum 後面,再加上 class 或 struct;而要使用定義的值的時候,一定要加上範圍(scope、這邊就是 class 的名稱)。
下面就是簡單的比較:
enum
|
enum class
|
---|---|
enum EColor { RED, GREEN, BLUE }; EColor eColor = RED; |
enum class EColor { RED, GREEN, BLUE }; EColor eColor = EColor::RED; |
為什麼要取代既有的 enum 呢?主要是 C++ 傳統的 enum 主要有三個問題:
- 預設可以被自動轉換成 int,在某些不應該被轉換成 int 的情況下,可能會造成問題
- 所列舉項目由於沒有特殊的範圍(scope),所以很容易造成衝突
- 沒辦法指定列舉型別底層的類型,容易造成混淆、不相容、同時也沒辦法做 forward declaration
在 LearnCpp.com 的《4.5a — Enum classes》這邊有舉一個傳統 enum 的例子(這邊有稍微改一下):
#include <iostream> enum EColor { RED, GREEN, BLUE }; enum EFruit { APPLE, BANANA }; int main() { EColor eColor = RED; EFruit eFruit = APPLE; if (eColor == eFruit) { std::cout << "color and fruit are equal" << std::endl; } else { std::cout << "color and NOT fruit are equal" << std::endl; } }
這樣的程式,執行的結果會「color and fruit are equal」。
其原因,就是系統會把「RED」和「APPLE」都直接當作 int 的數值來做比較;而在這個狀況下,兩者都是 0,所以就變成相等了。
造成的問題,就是理因不同型別的列舉型別實際上是可以拿來比較的,所以編譯、執行都不會出現特殊的警告,所以如果不小心誤用,會變得很難發現。
而如果改成強型別的列舉型別的話,則會變成
#include <iostream> enum class EColor { RED, GREEN, BLUE }; enum class EFruit { APPLE, BANANA }; int main() { EColor eColor = EColor::RED; EFruit eFruit = EFruit::APPLE; if (eColor == eFruit) { std::cout << "color and fruit are equal" << std::endl; } else { std::cout << "color and NOT fruit are equal" << std::endl; } }
這樣的寫法,在編譯階段,就會因為 eColor 和 eFruit 兩者屬於不同的型別,而無法正確編譯(上面紫色底那行)。
也由於這樣的錯誤會在編譯階段就被編譯器找到,所以基本上就不容易出現誤用到別的列舉型別的狀況了~
另外一種常見的問題,就是命名沖到了~
比如說下面的程式碼:
#include <iostream> enum EColor { RED, GREEN, BLUE }; enum EStatus { RED, YELLOW, GREEN }; int main() { EColor eColor = RED; }
這邊就會因為 RED 和 GREEN 在 EColor 和 EStatus 都被定義過,導致編譯氣會覺得他們被重複定義,而無法完成程式的編譯。
而由於 enum class 的寫法是包含了 scope 的概念,裡面所定義的值都是在 class 內的,所以就不會出現重複定義的問題了~
#include <iostream> enum class EColor { RED, GREEN, BLUE }; enum class EStatus { RED, YELLOW, GREEN }; int main() { EColor eColor = EColor::RED; }
另外,在 C++11 開始,不管是 enum 或 enum class,也都可以指定實際要使用的型別了。
比如說,可以指定他的底層要用 char 來記錄的話,就可以寫成:
enum class EColor : char { RED, GREEN, BLUE };
而有必要的話,也可以指定更大的型別來做紀錄。
參考:
- Better types in C++11 – nullptr, enum classes (strongly typed enumerations) and cstdint
- C++11 – the new ISO C++ standard:enum class — scoped and strongly typed enums
- LearnCpp.com:4.5a — Enum classes