使用 enum class 取代傳統的 enum

| | 0 Comments| 17:09
Categories:

「Scoped and strongly typed enums」是 C++11 時所引進的一個新的功能,主要是要取代舊的列舉型別(enum)。

他的基本用法,是在 enum 後面,再加上 classstruct;而要使用定義的值的時候,一定要加上範圍(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;
  }
}

這樣的寫法,在編譯階段,就會因為 eColoreFruit 兩者屬於不同的型別,而無法正確編譯(上面紫色底那行)。

也由於這樣的錯誤會在編譯階段就被編譯器找到,所以基本上就不容易出現誤用到別的列舉型別的狀況了~


另外一種常見的問題,就是命名沖到了~

比如說下面的程式碼:

#include <iostream>
enum EColor
{
  RED,
  GREEN,
  BLUE
};
enum EStatus
{
  RED,
  YELLOW,
  GREEN
};
int main()
{
  EColor eColor = RED;
}

這邊就會因為 REDGREENEColorEStatus 都被定義過,導致編譯氣會覺得他們被重複定義,而無法完成程式的編譯。

而由於 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
};

而有必要的話,也可以指定更大的型別來做紀錄。


參考:

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *