C++列舉型別同名衝突問題
今天被C++列舉型別困擾了……之前寫程式一直沒有留意到這個問題,原來在C++中使用多個列舉型別時也是不能出現同名的。DAMN IT!
個人認為這一點C++的實現確實是不如C#中那樣的列舉型別先進……當然這裡面是有一定的歷史原因的,要相容C嘛。但願C++的下一個版本中會進行改進(已經有這個兆頭了:-))
一時還真沒有發現什麼好的解決方案。只能是在列舉型別的前面多加字首了。網上倒是找到了幾種方案,不過我覺得都有一定的缺陷:
1.使用namespace。
如:namespace abc{enum exp{a,b,};}
不過我覺得在一個程式裡面寫的到處都是namespace無疑是一件非常尷尬的事,而且總有直覺認為在大型專案程式開發中會出現意想不到的錯誤……
2.定義一個巨集來完成一定的功能。(以下內容為COPY)
C++ 中的列舉型別繼承於 C 語言。就像其他從 C 語言繼承過來的很多特性一樣,C++ 列舉也有缺點,這其中最顯著的莫過於作用域問題——在列舉型別中定義的常量,屬於定義列舉的作用域,而不屬於這個列舉型別。例如下面的示例:
enum FileAccess {
Read = 0x1,
Write = 0x2,
};
FileAccess access = ::Read; // 正確
FileAccess access = FileAccess::Read; // 錯誤
C++列舉的這個特點對於習慣面向物件和作用域概念的人來說是不可接受的。首先,FileAccess::Read 顯然更加符合程式設計師的直覺,因為上面的列舉定義理應等價於如下的定義(實際上,.NET 中的列舉型別便是如此實現的):
class FileAccess {
static const int Read = 0x1;
static const int Write = 0x2;
};
其次,這導致我們無法在同一個作用域中定義兩個同樣名稱的列舉值。也就是說,以下的程式碼是編譯錯誤:
enum FileAccess {
Read = 0x1,
Write = 0x2,
};
enum FileShare {
Read = 0x1, // 重定義
Write = 0x2, // 重定義
};
如果這一點沒有讓你惱怒過的話,你可能還沒寫過多少 C++ 程式碼 :-)。實際上,在最新的 C++0x 標準草案中有關於列舉作用域問題的提案,但最終的解決方案會是怎樣的就無法未卜先知了,畢竟對於象 C++ 這樣使用廣泛的語言來說,任何特性的增刪和修改都必須十分小心謹慎。
當然,我們可以使用一些迂迴的方法來解決這個問題(C++ 總是能給我們很多驚喜和意外)。例如,我們可以把列舉值放在一個結構裡,並使用運算子過載來逼近列舉的特性:
struct FileAccess {
enum __Enum {
Read = 0x1,
Write = 0x2
};
__Enum _value; // 列舉值
FileAccess(int value = 0) : _value((__Enum)value) {}
FileAccess& operator=(int value) {
this->_value = (__Enum)value;
return *this;
}
operator int() const {
return this->_value;
}
};
我們現在可以按照希望的方式使用這個列舉型別:
FileAccess access = FileAccess::Read;
並且,因為我們提供了到 int 型別的轉換運算子,因此在需要 int 的地方都可以使用它,例如 switch 語句:
switch (access) {
case FileAccess::Read:
break;
case FileAccess::Write:
break;
}
當然我們不願意每次都手工編寫這樣的結構。通過使用巨集,我們可以很容易做到這一點:
#define DECLARE_ENUM(E) \
struct E \
{ \
public: \
E(int value = 0) : _value((__Enum)value) { \
} \
E& operator=(int value) { \
this->_value = (__Enum)value; \
return *this; \
} \
operator int() const { \
return this->_value; \
} \
\
enum __Enum {
#define END_ENUM() \
}; \
\
private: \
__Enum _value; \
};
我們現在可以按如下的方式定義前面的列舉,並且不比直接寫 enum 複雜多少。
DECLARE_ENUM(FileAccess)
Read = 0x1,
Write = 0x2,
END_ENUM()
DECLARE_ENUM(FileShare)
Read = 0x1,
Write = 0x2,
END_ENUM()
我覺得這種方法還好一些,不過看起來總是怪怪的,不符合標準C++的風格……只為了實現一個列舉的功能,還要搞出這樣的巨集來,還不如直接加前後綴簡捷直觀……
算了,老實定義不同名的列舉型別吧,要是有更好的解決方案,請留言……