Re: [分享] C++11 : Strongly typed enumerations

看板C_and_CPP作者 (reverse(wasefo);)時間9年前 (2014/09/11 00:02), 編輯推噓0(006)
留言6則, 1人參與, 最新討論串2/2 (看更多)
最近有用到 enum class 當做是某些值的 key 就有用到需要把 enum 內的值都走一遍 來檢查 KEY 對應的值有沒有存在 然後就有遇到 enum class 要巡迴 要轉換成 integer +1 會很麻煩 後來有參考到 StackOverflow 上的一些解法 "把他做成 Iterator" 這裡我有實作出一個簡單的 Enum Wrapper 可以 for(auto key: Enum<YOUR_ENUM>()){ ... } 就能夠走一遍 不過還是有一些限制 像是要連號要指定 First/Last 元素 但是己經可以省下一些轉型的手續 http://ot-note.logdown.com/posts/231687 ※ 引述《akasan (KITO)》之銘言: : 好讀版 : http://kitoslab.blogspot.tw/2012/09/c11-strongly-typed-enumerations.html : 因為我不太會上色所以這邊只有難讀版的XD.... : - 回顧 enum : 在 C++11 以前以下 enum 會污染整個 namespace : 大致的意思是如果你宣告了一個 enum E, 他有 A, B, C 三個值 : -------------------- Code ------------------ : enum E { : A, : B, : C, : }; : -------------------------------------------- : 那麼在這個 namespace 底下你就不能先告任何以 A, B, C 為 ID 型別或變數 : -------------------- Code ------------------ : enum E { : A, : B, : C, : }; : int A; /* Oop, compiler 會跟你抱怨 A 這個符號重複定義了!! */ : -------------------------------------------- : 而且很不近乎人情的你想要使用 E::A 的方式取值還會跟你抱怨 E 不是一個 namespace 或 class : (在 VC++ 中有 extension 讓你可以這樣取) : - C++11 前的折衷作法 : 這種情況事實上也不是不能解, 只要在 enum 外面包一層 namespace 即可, 但看起來會有點鳥就是了 : -------------------- Code ------------------ : namespace E { : enum Type { : A, : B, : C, : }; : } : int A; /* A 這個 ID 可以用了! */ : int var = E::B; /* 取用方式也以用 E:: 的方式取用 */ : E::Type e = E::A; /* 最大缺點就是 Type name 會變成 E::Type */ : -------------------------------------------- : - enum 的型別問題 : 前一段包一層 namespace 的作法可以解決大部分的問題, : 但是事實上 enum 還有一個問題就是, enum 常被拿來定義常數, : 但是他實際的型別不明!! : 在標準中也沒有規範 enum 要占多少大小 : 所以在大部分實作中, 大部分會以 int 來處理 : 在必要的時候他會長大成 64-bit 的 long long : -------------------- Code ------------------ : #include <stdio.h> : enum E1 { : A = 213, : B, : C, : }; : enum E2 { : D = 21343245325435ll, : E, : F, : }; : int main(){ : printf("%zd\n", sizeof(enum E1)); /* 印出 4 */ : printf("%zd\n", sizeof(enum E2)); /* 印出 8 */ : return 0; : } : /* 註: 環境是 Fedora 17 x86-64 : 編譯器為 g++ 4.6.3 以及 clang++ 3.2 */ : -------------------------------------------- : - enum 的型別問題在哪? : -------------------- Code ------------------ : enum E { : A = 0x0ea92d6f << 4, : B, : C, : }; : int main(){ : unsigned v = A; : switch (v) { : case A: /* gcc 4.6.3 可以 通過, clang 會跟你抱怨 - 359475472 沒辦法降轉到 unsigned! */ : break; : default: : break; : } : return 0; : } : -------------------------------------------- : -------------------- Code ------------------ : enum E { : A = 0x0ea92d6fu << 4, /*這邊加個 suffix u 就可解決 */ : B, : C, : }; : int main(){ : unsigned v = A; : switch (v) { : case A: : break; : default: : break; : } : return 0; : } : -------------------------------------------- : 當然第一眼看到可能會覺得這是編譯器實作問題, : 但是事實上最核心的問題是 : enum 的型別是啥 : 個人認為這等於在整個型別檢查上開了個漏洞...雖然 C 的 Type system 本來就很薄弱 : C++ 則是不幸的承受了 C 的一切 : - Strongly typed enumerations : C++11 中目前加入了 Strongly typed enumerations 以及一些語法上的改進 : 解決了上列所提到的問題點 : * ENUM::VAL : -------------------- Code ------------------ : enum E { : A = 0x0ea92d6f, : B, : C, : }; : int main(){ : int v1 = E::A; /* Ohhh 現在這是合法的使用方式了 */ : int v2 = A; /* 舊有取值方式向下相容 */ : return v; : } : -------------------------------------------- : * 型別問題 : -------------------- Code ------------------ : enum E : long long { /* 可以明確指定底層使用型別! */ : A = 123, : B, : C, : }; : int main() { : printf("%zd\n", sizeof(E)); /* 8 ! */ : return 0; : } : -------------------------------------------- : * Strong Type : enum 除了可以拿來當常數宣告外, : 在只拿來當列舉值時, 你可能就不會喜歡他能跟類 int 型別互轉不用錢的特性了 : -------------------- Code ------------------ : enum class E { /* 加個 class enum 換成 Strong Type! */ : A = 123, : B, : C, : }; : int main() { : int val = E::A; /* 跟你抱怨不能從 E 強轉成 int */ : return 0; : } : -------------------------------------------- : -------------------- Code ------------------ : enum class E { /* 加個 class enum 換成 Strong Type! */ : A = 123, : B, : C, : }; : int main() { : int val = E::A; /* 跟你抱怨不能從 E 強轉成 int */ : return 0; : } : -------------------------------------------- : 接著他也可以跟前面指定形別的語法結合 : -------------------- Code ------------------ : enum class E : long long { /* 加個 class enum 換成 Strong Type! : 並且指定為 long long */ : A = 123, : B, : C, : }; : -------------------------------------------- : 最後要注意一點的是雖然他加了個 class, : 不過他依然只是 enum, 不能有 member data 及 member function : 補充: : g++ -std=c++0x 或 g++ -std=c++11 : clang++ -std=c++0x 或 clang++ -std=c++11 : 可以切成 C++11 模式, 較舊clang/gcc版本需要使用前者 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 115.43.44.133 ※ 文章網址: http://www.ptt.cc/bbs/C_and_CPP/M.1410364921.A.A19.html

09/14 04:03, , 1F
在沒有 compile-time reflection 之前看到的解決方法
09/14 04:03, 1F

09/14 04:04, , 2F
大多還是以 MACRO 產生包含所有值的 array 爲主
09/14 04:04, 2F

09/14 04:06, , 3F
不過通常會先避免需要 iterate 所有值的情況。
09/14 04:06, 3F

09/14 04:07, , 4F
記得之前有看到用 macro 產生類似 java enum util 的
09/14 04:07, 4F

09/14 04:16, , 5F
有點類似這樣 但是更完整(支援自訂值和 namespace 等
09/14 04:16, 5F

09/14 04:16, , 6F
09/14 04:16, 6F
文章代碼(AID): #1K47NveP (C_and_CPP)
文章代碼(AID): #1K47NveP (C_and_CPP)