2013-07-23 68 views
13

如果將整型轉換爲枚舉類,但該值不在枚舉中,會發生什麼?例如:我想是測試如果一個整數具有從枚舉類的一些值的函數:在枚舉中轉換整數的安全方法

enum class EnumClass { A, B = 4, C = 9, D = 60 }; 

bool checkEnumClass(int v) 
{ 
    switch(static_cast<EnumClass>(v)) 
    { 
    case EnumClass::A: 
    case EnumClass::B: 
    case EnumClass::C: 
    case EnumClass::D: 
     return true; 
    default: 
     return false; 
    } 
} 

checkEnumClass(0) == true; 
checkEnumClass(7) == false; // is this true? 

這是爲了檢查一個整數轉換爲一個枚舉的正確方法?

+0

爲什麼不使用checkEnumClass(EnumClass)? – camino

+0

因爲我必須解析一些整數...並檢查(或更好地保證)整數可轉換爲枚舉 –

+0

enum可以在其最小值和最大值之間保存任何值:http://stackoverflow.com/a/ 7676940/951890 –

回答

6

枚舉可以保持其最小值和最大值之間的任何值,所以你擁有的主要是正確的。您還需要做的唯一的事情是確保整數參數是在適當的範圍內,因爲如果試圖施放一個int是枚舉的範圍之外,你有不確定的操作:

bool checkEnumClass(int v) 
{ 
    if (v < static_cast<int>(EnumClass::A)) return false; 
    if (v > static_cast<int>(EnumClass::D)) return false; 

    switch(static_cast<EnumClass>(v)) 
    { 
    case EnumClass::A: 
    case EnumClass::B: 
    case EnumClass::C: 
    case EnumClass::D: 
     return true; 
    default: 
     return false; 
    } 
} 
+0

範圍檢查不會被交換機默認值捕獲嗎?或者我錯過了什麼? – firebush

+2

投射一個超出枚舉值範圍的int是不合法的,因此必須先檢查它。 –

+0

我明白了。如果您嘗試從枚舉的最小值和最大值以外的整數值轉換爲EnumClass,則結果爲未定義行爲。請參見前幾段[這裏](http://stackoverflow.com/a/18195408/629530)。感謝你的回答。 – firebush

-1

只需檢查int是否不大於檢查類中的最大可能值,不需要switch語句,只需使用if語句,或者更好,只是一個布爾值。

bool checkEnumClass(int i) 
{ 
    return (i <= 7); 
} 
+1

我編輯了這個問題。如果枚舉有一些「漏洞」,則更多我不能只說我<= n –

+0

爲什麼你想讓你的枚舉包含漏洞?通常情況下,如果您要表示一個值列表(例如狀態代碼等)),那麼你只需要使用常量。 – drz

+2

這個枚舉提供了一些不疲勞的常量值。這不是我的選擇有這種枚舉 –

10

我沒有看到比OP提供的解決方案更好的解決方案。但是,它有一個小缺陷,我可以提出一個(非標準)解決方法。

問題如下。假設今天的代碼是在OP但是,有一天,有人增加了一個新的枚舉EnumClass成爲:

enum class EnumClass { A, B = 4, C = 9, D = 60, E = 70 }; 

還假設這個人忘記更新的checkEnumClass的定義(這不是不可能發生,特別是如果代碼在另一個文件中)。然後,

checkEnumClass(70); 

會盡管70現在是一個有效的返回值false。單元測試可能有助於捕捉這個錯誤,但是人員必須記得更新測試。 (回想一下,他們忘放在第一位更新代碼!)

不幸的是,標準C++不提供一種方法來強制switchenum覆蓋所有的情況下(與d它提供了final switchstatement) 。

但是,有編譯器特有的功能可以爲您做到這一點。您可以添加編譯器選項-Wswitch(或-Wall,這意味着-Wswitch)。對於GCC(以及我相信,鐺,以及)也可以添加編譯器選項-Wswitch。對於Visual Studio,您可以添加

#pragma warning(error : 4062) 

checkEnumClass包含枚舉定義文件)

最後的文件,你必須稍微改變checkEnumClass因爲default標籤告訴編譯器,所有病例被覆蓋的。該代碼應該是這樣的:

bool checkEnumClass(int v) 
{ 
    switch(static_cast<EnumClass>(v)) 
    { 
    case EnumClass::A: 
    case EnumClass::B: 
    case EnumClass::C: 
    case EnumClass::D: 
     return true; 
    } 
    return false; 
} 

有了這個解決辦法,誰包括枚舉E但忘了更新checkEnumClass因此將得到下面的錯誤/警告的人:

GCC:

警告:枚舉值'E'未在開關中處理[-Wswitch]

Visual Stud IO:

錯誤C4062:枚舉 'EnumClass' 的開關枚舉 'E' 不被處理
開關(的static_cast < EnumClass>(V))

更新1:繼由elvis.dukaj發表評論。

作爲一個良好的做法,將-Werror添加到海灣合作委員會的選項將所有警告轉化爲錯誤。

更新2:比-Wswitch更好的爲-Wswitch-enum這將提高(如果-Werror或錯誤)的警告,即使有一個default標籤。不幸的是,我不知道Visual Studio中的任何類似功能。

+0

我總是 - 牆 - 錯誤,所以我啓用所有的警告和特點他們作爲錯誤(大量的愚蠢錯誤以這種方式避免)。 –

+0

@ elvis.dukaj:這是一個很好的做法(我也遵循),但是對於這個特殊的問題(一個'enum'的開關不包含所有的枚舉器),否則至關重要的是沒有'default'標籤'-Wswitch'不會幫助:-( –

+0

@ elvis.dukaj:實際上,我找到了一種方法(並相應地更新了答案),以使'default'標籤不會阻止警告/錯誤。感謝您的評論。 –

4

如果你需要編譯時枚舉值的檢查,你可以試試這個:

template <int I> struct check_enum { static const bool value = false; }; 

template <> struct check_enum<static_cast<int>(EnumClass::A)> 
{ static const bool value = true; }; 

template <> struct check_enum<static_cast<int>(EnumClass::B)> 
{ static const bool value = true; }; 

template <> struct check_enum<static_cast<int>(EnumClass::C)> 
{ static const bool value = true; }; 

template <> struct check_enum<static_cast<int>(EnumClass::D)> 
{ static const bool value = true; }; 

然後,您可以使用這種方式:

static_assert(check_enum<0>::value, "invalid enum value"); // ok! 
static_assert(check_enum<1>::value, "invalid enum value"); // compile error 

Live demo

編輯:使用C++ 14模板變量也可以使用相同的方法。

template <int I> constexpr bool check_enum = false; 
template <> constexpr bool check_enum<static_cast<int>(EnumClass::A)> = true; 
template <> constexpr bool check_enum<static_cast<int>(EnumClass::B)> = true; 
template <> constexpr bool check_enum<static_cast<int>(EnumClass::C)> = true; 
template <> constexpr bool check_enum<static_cast<int>(EnumClass::D)> = true; 

static_assert(check_enum<0>, "invalid enum value"); // ok! 
static_assert(check_enum<1>, "invalid enum value"); // compile error 

這些方法的主要缺點是專門化每個值的努力,你必須認爲如果努力是值得的。如果錯過了一些價值,那麼可能很難找到並解決問題。

+0

如果有那麼多的枚舉值,那麼這種方法將不實用 – iammilind

+0

@iammilind:就像我說的: *這種方法的主要缺點是代碼臃腫,並且需要爲每個有效值專門設置模板* –

+0

您的方法沒有代碼膨脹,編譯後所有內容都進行了優化上。唯一的問題是專門化每一個不值得花費的價值。如果錯過了一些價值,那麼很難解決問題。 – iammilind