2009-11-26 59 views
59

Có cách nào trong C++ để mở rộng/"kế thừa" enums?Mở rộng enums trong C++?

tức là:

enum Enum {A,B,C}; 
enum EnumEx : public Enum {D,E,F}; 

hoặc ít nhất là xác định một sự chuyển đổi giữa chúng?

+2

tôi đăng một C++ 11 câu trả lời cho http://stackoverflow.com/questions/14503620/extending-enum-types/14508431 # 14508431 – dspeyer

+0

Có một bài viết rất thú vị, với một số mã, tại đây: http://www.codeproject.com/Articles/32000/Improving-C-Enums-Adding-Serialization-Inheritance –

Trả lời

53

Không, không có.

enum thực sự là điều tồi tệ trong C++ và điều đó thật không may.

Ngay cả các class enum được giới thiệu trong C++ 0x không giải quyết vấn đề mở rộng này (mặc dù chúng làm một số điều cho an toàn kiểu ít nhất).

Lợi thế duy nhất của enum là chúng không tồn tại: chúng cung cấp một số loại an toàn trong khi không áp đặt bất kỳ chi phí thời gian chạy nào khi chúng được thay thế trực tiếp bởi trình biên dịch.

Nếu bạn muốn có một con quái vật như vậy, bạn sẽ phải làm việc cho mình:

  • tạo ra một lớp MyEnum, có chứa một int (về cơ bản)
  • tạo tên nhà thầu cho mỗi người trong số các giá trị thú vị

bây giờ bạn có thể mở rộng lớp học của bạn (thêm constructors tên) theo ý muốn ...

Đó là một cách giải quyết, mặc dù tôi chưa bao giờ người sáng lập da satistifying cách đối phó với một đếm ...

+0

Than ôi, tôi đồng ý. Vì vậy, nhiều thời gian tôi đã muốn có một loại an toàn cách đi qua cờ vv nhưng enums thực sự chỉ là ints. –

-3

Mã sau hoạt động tốt.

enum Enum {A,B,C}; 
enum EnumEx {D=C+1,E,F}; 
+8

Không thực sự hiệu quả . A vẫn là một Enum và không phải là EnumEx. I E. EnumEx x = A; sẽ không biên dịch mà không có dàn diễn viên. –

+2

Thực ra, tôi quan tâm nhiều hơn về các tác động lên hệ thống kiểu, thay vì việc lập chỉ mục các giá trị. – cvb

7

Nếu bạn có thể tạo một lớp con của enum, nó sẽ phải hoạt động theo cách khác.

Tập hợp các phiên bản trong một phân lớp là một tập hợp con trong các trường hợp trong siêu hạng. Hãy nghĩ về ví dụ "Hình dạng" chuẩn. Lớp Shape đại diện cho tập hợp tất cả các Shapes. Lớp Circle, lớp con của nó, đại diện cho tập hợp con của Hình dạng là Vòng kết nối.

Vì vậy, để nhất quán, một phân lớp của một enum sẽ phải chứa một tập con của các phần tử trong enum mà nó kế thừa từ.

(Và không, C++ không hỗ trợ điều này.)

+3

Đó là cách giải thích rất cụ thể về thừa kế, điều đó không nhất thiết áp dụng cho tất cả các ứng dụng thừa kế. Tôi không nghĩ rằng nó thực sự giải thích tại sao một trình biên dịch không thể hỗ trợ enum EnumEx: public Enum {D, E, F}; sao cho một EnumEx có thể được truyền cho một hàm mong đợi một Enum. –

+1

@jon: vì nó sẽ phá vỡ nguyên tắc thay thế Liskov: D sẽ "là" Enum (vì nó thừa kế từ nó), nhưng nó sẽ không có bất kỳ giá trị hợp lệ nào của Enum. Mâu thuẫn. enums được thiết kế để được "liệt kê các loại" - toàn bộ điểm là bạn xác định loại bằng cách xác định tất cả các đối tượng hợp lệ của loại đó (trong trường hợp của C/C++, thực sự bản đồ từ các giá trị được liệt kê cho tất cả các loại hợp lệ là một chút lạ và liên quan đến loại được sử dụng để đại diện cho enum). Đây có thể là một cách giải thích rất cụ thể về thừa kế, nhưng nó là một giải pháp tốt, và AFAIK nó thông báo cho thiết kế của C++. –

+1

Như một ví dụ cụ thể mà nó sẽ phá vỡ, giả sử tôi định nghĩa một 'enum A {1, 65525};' và trình biên dịch quyết định sử dụng một 16 bit unsigned int để biểu diễn nó. Bây giờ giả sử tôi định nghĩa 'enumEx: public Enum {131071};'. Không có cách nào đối tượng này của kiểu EnumEx có thể được truyền như một thể hiện của Enum, nó sẽ có hiệu lực được cắt lát. Rất tiếc. Đây là lý do tại sao bạn cần con trỏ trong C++ để làm đa hình thời gian chạy. Tôi đoán C++ có thể làm cho mọi enum kích thước của enum lớn nhất có thể. Nhưng về mặt khái niệm, giá trị 131071 không nên là một ví dụ hợp lệ của Enum. –

6

Tôi đã giải quyết theo cách này:

typedef enum 
{ 
    #include "NetProtocols.def" 
} eNetProtocols, eNP; 

Tất nhiên, nếu bạn thêm một giao thức mạng mới trong file NetProtocols.def, bạn phải biên dịch lại, nhưng ít nhất nó có thể mở rộng.

2

http://www.codeproject.com/KB/cpp/InheritEnum.aspx đi qua một phương pháp để tạo ra một enum mở rộng.

+1

Một phương pháp rất nguy hiểm. Nếu hai enums được đặt lại với nhau sử dụng cùng một giá trị, điều này âm thầm confuses chúng. – dspeyer

+1

@ dspeyer nó vẫn mang lại một ý tưởng thú vị cho toàn bộ cuộc tranh luận. giải pháp của bạn là con của điều này. –

0

Chỉ cần một ý tưởng:

Bạn có thể cố gắng tạo ra một lớp học trống cho mỗi liên tục (có thể đặt chúng tất cả trong cùng một tập tin để giảm lộn xộn), tạo ra một thể hiện của mỗi lớp và sử dụng các con trỏ đến những trường hợp này là "hằng số".Bằng cách đó, trình biên dịch sẽ hiểu được kế thừa và sẽ thực hiện bất kỳ chuyển đổi ChildPointer-to-ParentPointer nào cần thiết khi sử dụng các cuộc gọi hàm, và bạn vẫn nhận được kiểm tra an toàn loại bởi trình biên dịch để đảm bảo không có ai vượt qua giá trị int không hợp lệ cho các hàm. được sử dụng nếu bạn sử dụng phương thức giá trị LAST để "mở rộng" enum).

Chưa hoàn toàn nghĩ về điều này mặc dù vậy mọi nhận xét về phương pháp này đều được hoan nghênh.

Và tôi sẽ cố gắng đăng một ví dụ về ý của tôi ngay khi tôi có thời gian.

0

Tôi cố gắng này, nó hoạt động, nhưng nó là một chút tẻ nhạt:

#include <iostream> 

struct Foo { 
protected: 
    int _v; 
    Foo(int i) : _v(i) {} 

public: 
    operator int() const { return _v; } 

    static Foo FOO() { return 5; }; 
}; 

struct Bar : public Foo { 
    using Foo::Foo; 
    Bar(const Foo &f) : Foo(f) {} 

    static Bar BAR() { return 6; }; 
}; 

int main(int argc, const char *argv[]) { 
    // Bar bar = Bar::BAR(); 
    Foo foo = Foo::FOO(); 
    Bar bar = Bar::BAR(); 

    // 5 
    Bar b = foo; 
    std::cout << (int)b << std::endl; 

    // 6 
    b = bar; 
    std::cout << (int)b << std::endl; 

    // 5 
    b = foo; 
    std::cout << (int)b << std::endl; 

    // Foo err1(5); 
    // Bar err2(5); 
    return 0; 
}