2009-11-26 4 views
59

Есть ли способ в C++ расширять/"наследовать" перечисления?Расширение перечислений на C++?

т.е:

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

или по крайней мере определить преобразование между ними?

+2

Я отправил C++ 11 Ответ на http://stackoverflow.com/questions/14503620/extending-enum-types/14508431 # 14508431 – dspeyer

+0

Существует очень интересная статья с некоторым кодом: http://www.codeproject.com/Articles/32000/Improving-C-Enums-Adding-Serialization-Inheritance –

ответ

53

Нет, нет.

enum действительно бедны на Си ++, и это, к сожалению, конечно.

Даже class enum, представленный в C++ 0x, не затрагивает эту проблему расширяемости (хотя они делают некоторые вещи для безопасности по типу как минимум).

Единственное преимущество enum заключается в том, что они не существуют: они предлагают некоторую безопасность типов, не налагая накладные расходы во время выполнения, поскольку они подставляются непосредственно компилятором.

Если вы хотите такого зверя, вам придется работать самостоятельно:

  • создать класс MyEnum, который содержит Int (в основном)
  • создавать именованные конструкторы для каждого из интересных значений

теперь вы можете расширить класс (добавление именованные конструкторы) по желанию ...

Это обходной путь, хотя, я никогда не foun да satistifying способ борьбы с перечислением ...

+0

Увы, согласен. Так много раз я хотел, чтобы безопасный способ пропускать флаги и т. Д., Но перечисления действительно просто ints. –

-3

Следующий код работает хорошо.

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

На самом деле не работает , A по-прежнему является Enum, а не EnumEx. То есть EnumEx x = A; не будет компилироваться без броска. –

+2

На самом деле, меня больше беспокоят последствия для системы типов, а не индексации значений. – cvb

7

Если вам удалось создать подкласс перечисления, ему пришлось бы работать наоборот.

Набор экземпляров в подклассе представляет собой подмножество экземпляров в суперклассе. Подумайте о стандартном примере «Shape». Класс Shape представляет набор всех Shapes. Класс Circle, его подкласс, представляет собой подмножество Shapes, которые являются кругами.

Чтобы быть последовательным, подкласс перечисления должен содержать подмножество элементов в перечислении, на которое он наследует.

(И нет, C++ не поддерживает это.)

+3

Это очень специфическая интерпретация наследования, которая не обязательно применима ко всем видам использования наследования. Я не думаю, что это действительно объясняет, почему компилятор не мог поддерживать enum EnumEx: public Enum {D, E, F}; так что EnumEx может быть передан функции, ожидающей Enum. –

+1

@jon: потому что он нарушит принцип замещения Лискова: D будет «enum» (потому что он наследует его), но он не будет иметь никаких допустимых значений Enum. Противоречие. перечисления предназначены для «перечисляемых типов» - все дело в том, что вы определяете тип, определяя все допустимые объекты этого типа (в случае C/C++ фактически отображение из перечисленных значений во все допустимые типы немного странно , и включает в себя тип, используемый для представления перечисления). Это может быть очень специфическая интерпретация наследования, но она хорошая, и AFAIK он сообщает о дизайне C++. –

+1

В качестве конкретного примера, где он будет разбит, предположим, что я определяю 'enum A {1, 65525};', и компилятор решает использовать 16-битный unsigned int для его представления. Теперь предположим, что я определяю 'enumEx: public Enum {131071};'. Нет никакого способа, чтобы этот объект типа EnumEx мог быть передан как экземпляр Enum, он фактически был бы нарезанным. К сожалению. Вот почему вам нужны указатели на C++ для выполнения полиморфизма во время выполнения. Я предполагаю, что C++ может сделать каждый перечислитель размером с максимально возможным перечислением. Но концептуально говоря, значение 131071 не должно быть действительным экземпляром Enum. –

6

Я решил таким образом:

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

Конечно, если вы добавляете новый чистый протокол в NetProtocols.def файл, вы должны перекомпилировать, но, по крайней мере, он расширяем.

2

http://www.codeproject.com/KB/cpp/InheritEnum.aspx рассматривает метод создания расширенного перечисления.

+1

Очень опасный метод. Если два перечисления, которые собираются вместе, используют одно и то же значение, это их смущает. – dspeyer

+1

@dspeyer все еще представляет интересную идею для всей дискуссии. ваше решение является отпрыском этого. –

0

Просто идея:

Вы могли бы попытаться создать пустой класс для каждой константы (возможно поместить их все в одном файле, чтобы уменьшить беспорядок), создать один экземпляр каждого класса и использовать указатели на эти экземпляры как «константы».Таким образом, компилятор поймет наследование и выполнит любое преобразование ChildPointer-to-ParentPointer, необходимое при использовании вызовов функций, и вы все равно получите проверки безопасности типа компилятором, чтобы гарантировать, что никто не передает недопустимое значение int для функций (что для использования, если вы используете метод LAST value для «расширения» перечисления).

Не подумал об этом, хотя любые замечания по этому подходу приветствуются.

И я попытаюсь опубликовать пример того, что я имею в виду, как только у меня есть время.

0

Я попробовал это, она работает, но это немного утомительно:

#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; 
} 
Смежные вопросы