2013年2月14日 星期四

C++11 Strongly typed enumeratio

作者: akasan (KITO) 看板: C_and_CPP
標題: [分享] C++11 : Strongly typed enumerations
時間: Thu Sep 20 00:22:16 2012


好讀版 : 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

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)
◆ From: 114.36.174.207
※ 編輯: akasan          來自: 114.36.174.207       (09/20 00:22)
推 wangm4a1:推                                                     09/20 00:37
推 BlazarArc:推 我是都把Enum包在Class裡面(非c++11)                 09/20 00:48
推 BlazarArc:用namespace包, 閱讀會向 Color::Type color=Color::RED  09/20 00:52
→ DEATHX:上次在改freerdp時才遇到這個問題,裡面用enum定義遠端桌面  09/20 05:56
→ DEATHX:協定裡的標準header,我還在想為何不用define要用enum。     09/20 05:58
→ uranusjr:Strong typed enum 指定型別的意義是什麼啊, 單純就可以   09/20 07:45
→ uranusjr:列舉比較大的數字而已嗎...                              09/20 07:45
→ loveme00835:to uran: http://ppt.cc/eVqX                         09/20 08:54

沒有留言:

張貼留言

您好.本資料庫並非第一手資料.如果你有對文章作者的詢問,意見與需求,請自行找尋文章作者並提供意見,謝謝.