2012-12-20 53 views
4

我的C++的強類型的特點和我最喜歡的是在處理有限組數據的使用枚舉的忠實粉絲。使用枚舉在循環和價值的一致性

但枚舉缺少一些實用的功能,例如運營商:

enum class Hex : int 
{ 
    n00, n01, n02, n03, 
    n04, n05, n06, n07, 
    n08, n09, n10, n11, 
    n12, n13, n14, n15 
}; 

for (Hex h = Hex::n0; h <= Hex::n15; ++h) // Oops! no 'operator ++' 
{ /* ... */ } 

是容易擺脫缺乏運營商建立在相同的範圍內自由操作的:

Hex &operator ++(Hex &h) 
{ 
    int r = static_cast<int>(Hex); 
    h = static_cast<Hex>(r + 1); 
    return h; 
} 

for (Hex h = Hex::n0; h <= Hex::n15; ++h) // Now the '++h' works! 
{ 
    std::cout << std::dec << int(h) << ": " 
       << std::hex << int(h) << '\n'; 
} 

但這方法是多解討厭,因爲它可以打破枚舉值限制:申請++hh等於Hex::n15將設置小時,他值16,至極超出的而h值範圍仍然是類型Hex的,這個問題在其他枚舉更加明顯:

enum class Prime : int 
{ 
    n00 = 2, n01 = 3, n02 = 5, n03 = 7, 
    n04 = 11, n05 = 13, n06 = 17, n07 = 19, 
    n08 = 23, n09 = 29, n10 = 31, n11 = 37, 
    n12 = 41, n13 = 43, n14 = 47, n15 = 53 
}; 

Prime &operator ++(Prime &p) 
{ 
    // How to implement this?! will I need a lookup table? 
    return p; 
} 

這個問題是個驚喜給我。我打賭,將不正確的值存儲到枚舉值中會引發異常。所以,現在我想知道是否有處理這個枚舉的弱點優雅的方式,我想達到的目標是:

  • 查找循環使用枚舉值一個舒適的方式。
  • 確保操作之間的enumation數據一致性。

其他問題:

  • 是否有當一個枚舉數據得到一個值超出其可能的值不是拋出一個異常的原因是什麼?
  • 有推斷與枚舉類相關聯的類型的?類型int在枚舉HexPrime的一種方式。
+2

如果你想迭代,那麼不要使用枚舉。 – Pubby

+0

功能泄漏確實是相當危險的...... –

+2

可以證明,第一個問題僅僅是因爲你是在吸引枚舉 - 因爲你用它們來枚舉整數!用實際整數更好。第二個問題(列舉素數)是不平凡的。 –

回答

1

您可以使用switch

class Invalid {}; 
Prime& operator ++(Prime& p) 
{ 
    switch(p) 
    { 
     case n00: return n01; 
     case n01: return n02; 
     case n02: return n03; 
     case n03: return n04; 
     case n04: return n05; 
     case n05: return n06; 
     case n06: return n07; 
     case n07: return n08; 
     case n08: return n09; 
     case n09: return n10; 
     case n10: return n11; 
     case n11: return n12; 
     case n12: return n13; 
     case n13: return n14; 
     case n14: return n15; 
     // Here: 2 choices: loop or throw (which is the only way to signal an error here) 
     case n15: default: throw Invalid(); 
    } 
} 

但是請注意,這是不使用權枚舉。我個人發現這個容易出錯。如果你想枚舉整數,你可以使用一個整數數組來做到這一點,或者對於素數的情況下,一個函數(在數學意義上:int nextPrime(int))。

+0

'if(p!= n15)return(Hex)((int)p + 1);拋出無效();'。 –

+0

號碼:例:用於'P = n09','(INT)p == 29',所以'(INT)的p + 1 == 30',並且不必在'Prime'具有任何枚舉值值30. 30. – Synxis

+0

對不起,我是個白癡。我沒有讀過這個問題的後半部分,我只是在想'十六進制'。 –

4

正如你已經注意到,在enum C++是枚舉類型, 但更復雜的東西(或多種混合)。當你定義一個 enum,您實際上定義了兩件事情:

  1. 整型具有合法範圍足以容納一個 或全部枚舉值。 (技術上:範圍 是2^n - 1,其中n必要 的比特數保持的最大值。)

  2. 一系列具有新定義類型的命名常量。

(我不知道與問候的範圍內會發生什麼,如果你 明確指定一個基本類型。)

鑑於你enum Prime,例如,法律價值將 所有整數中範圍[0...64),即使所有這些 值都沒有名稱。 (至少如果你沒有專門 說,它應該是一個int

這是可以實現的枚舉迭代器不 初始化;我有一個程序可以生成必要的 代碼。但它的工作原理是將值保持在一個整數型 ,該值足夠大以包含最大值加1。我的 機器生成的實現++這樣的枚舉將 assert如果您嘗試增加超過結束。 (請注意, 你的第一個例子就是需要迭代h一舉超越 最後一個值:我實現各運營商的不 允許這樣做,這就是爲什麼我使用一個迭代器)

至於爲何C++支持擴展範圍:enum經常被用來 定義位掩碼:

enum X 
{ 
    a = 0x01, 
    b = 0x02, 
    c = 0x04, 
    all = a | b | c, 
    none = 0 
}; 

X operator|(X lhs, X rhs) 
{ 
    return X((int)lhs | (int)rhs); 
} 
// similarly, &, |= and &=, and maybe ~ 

有人可能會認爲,這將使用由 可以更好地處理一類,但它使用的enum是無處不在的。

(FWIW:我的代碼生成器不會產生++--和 迭代器,如果任何枚舉值都有一個明確 定義的值,且不會產生|&等,除非所有的 值有明確定義的值)

至於爲何沒有錯誤時將轉換爲一定的價值外 法律範圍(例如100,爲X,以上)僅僅是在保持 會同C繼承了一般的理念:它的更好要比 快是正確的。做額外的範圍檢查 需要額外的運行成本。

最後關於你的最後一個例子:我不認爲這是 現實使用enum。這裏的正確解決方案是 int[]。雖然C++ enum是一個混合品種,但我會 只使用它作爲一個真正的枚舉類型,或位掩碼(和 只用於位掩碼,因爲它是如此廣泛建立 成語)。

+1

具有指定的基礎類型的C++ 11枚舉當然具有基礎類型的範圍。 – PlasmaHH

+0

@PlasmaHH這似乎是合乎邏輯的,但是如果沒有真正檢查它,人們永遠都不會知道。 (如果我想強制一個特定的底層類型,我看不到任何理由使用'enum'。) –

+0

將枚舉用作位掩碼的常量時它可能很有意義。 – PlasmaHH