Наследование класса базового перечисления


79

Есть ли шаблон, по которому я могу наследовать перечисление от другого перечисления в C ++?

Что-то такое:

enum eBase 
{
   one=1, two, three
};


enum eDerived: public eBase
{
   four=4, five, six
};

Ответы:


67

Невозможно. С перечислениями наследования нет.

Вместо этого вы можете использовать классы с именами const ints.

Пример:

class Colors
{
public:
  static const int RED = 1;
  static const int GREEN = 2;
};

class RGB : public Colors
{
  static const int BLUE = 10;
};


class FourColors : public Colors
{
public:
  static const int ORANGE = 100;
  static const int PURPLE = 101;
};

Есть ли проблема с этим решением? Например, (у меня нет глубокого понимания полиморфизма) Можно ли иметь вектор <Colors> и использовать p = std :: find (mycolors, mycolor + length, Colors :: ORANGE) ;?
— jespestana

1
@jespestana Нет, вы не будете использовать Colorsэкземпляры классов. Вы используете только значения int в статических константных членах.
— jiandingzhe

Если я правильно вас понимаю; тогда мне пришлось бы использовать контейнер vector <Int>. Но я все равно мог бы написать: p = std :: find (mycolors, mycolor + length, Colors :: ORANGE) ;. правильно?
— jespestana

1
@jespestana абсолютно. также, если поиск - очень распространенная операция, рассмотрите возможность использования flat_set или хеш-набора открытых адресов.
— v.oddou

1
Re: Есть ли проблемы с этим решением? Может возникнуть проблема, если эти значения больше не относятся к определенному типу. Вы не могли бы написать функцию, которая ожидает Color, как вы могли бы для enum.
— Дрю

93
#include <iostream>
#include <ostream>

class Enum
{
public:
    enum
    {
        One = 1,
        Two,
        Last
    };
};

class EnumDeriv : public Enum
{
public:
    enum
    {
        Three = Enum::Last,
        Four,
        Five
    };
};

int main()
{
    std::cout << EnumDeriv::One << std::endl;
    std::cout << EnumDeriv::Four << std::endl;
    return 0;
}

1
Я не совсем понимаю! Как бы вы затем сослались на типы Enum в аргументе переменной или функции и как обеспечить, чтобы функция, ожидающая Enum, не получила EnumDeriv?
— Sideshow Боб

21
Это не сработает. Когда вы определяете некоторые функции int basic(EnumBase b) { return b; }и int derived(EnumDeriv d) { return d; }, эти типы не будут преобразованы в int, хотя простые перечисления могут. И когда вы пытаетесь даже такой простой код , как этот: cout << basic(EnumBase::One) << endl;, то вы получите сообщение об ошибке: conversion from ‘EnumBase::<anonymous enum>’ to non-scalar type ‘EnumBase’ requested. Эти проблемы, вероятно, можно решить, добавив некоторые операторы преобразования.
— SasQ

10

Вы не можете сделать это напрямую, но можете попробовать использовать решение из этой статьи.

Основная идея - использовать вспомогательный шаблонный класс, который содержит значения перечисления и имеет оператор приведения типа. Учитывая, что базовый тип для enum - intвы можете легко использовать этот класс-держатель в своем коде вместо enum.


Хотя этот фрагмент кода может решить вопрос, включение объяснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин вашего предложения кода.
— Натан Оливер,

Это отличный ответ; это один из тех случаев, когда «подумайте о проблеме по-другому», и идея использования шаблона действительно отвечает всем требованиям.
— Ден-Джейсон

Также ознакомьтесь с некоторыми решениями для этих шаблонов vis-a-vis: stackoverflow.com/questions/5871722/…
— Ден-Джейсон

5

К сожалению, в C ++ 14 это невозможно. Я надеюсь, что у нас будет такая языковая функция в C ++ 17. Поскольку у вас уже есть несколько способов решения вашей проблемы, я не буду предлагать решение.

Хочу отметить, что формулировка должна быть «расширение», а не «наследование». Расширение позволяет использовать больше значений (поскольку в вашем примере вы перескакиваете с 3 на 6 значений), тогда как наследование означает наложение большего количества ограничений на данный базовый класс, поэтому набор возможностей сокращается. Следовательно, потенциальное приведение будет работать прямо противоположно наследованию. Вы можете привести производный класс к базовому классу, а не наоборот с наследованием классов. Но при наличии расширений вы «должны» иметь возможность преобразовывать базовый класс в его расширение, а не наоборот. Я говорю «следует», потому что, как я уже сказал, такой языковой функции все еще не существует.


Обратите внимание, что extendsэто ключевое слово для наследования в языке Eiffel.
— Приветствия и hth. - Альф,

Вы правы, потому что в этом случае не соблюдается принцип замещения Лискова. По этой причине комитет не примет решение, которое синтаксически выглядит как наследование.
— v.oddou

4

Как насчет этого? Хорошо, экземпляр создается для всех возможных значений, но, кроме того, он очень гибкий. Есть ли минусы?

.час:

class BaseEnum
{
public:
  static const BaseEnum ONE;
  static const BaseEnum TWO;

  bool operator==(const BaseEnum& other);

protected:
  BaseEnum() : i(maxI++) {}
  const int i;
  static int maxI;
};

class DerivedEnum : public BaseEnum
{
public:
  static const DerivedEnum THREE;
};

.cpp:

int BaseEnum::maxI = 0;

bool BaseEnum::operator==(const BaseEnum& other) {
  return i == other.i;
}

const BaseEnum BaseEnum::ONE;
const BaseEnum BaseEnum::TWO;
const DerivedEnum DerivedEnum::THREE;

Применение:

BaseEnum e = DerivedEnum::THREE;

if (e == DerivedEnum::THREE) {
    std::cerr << "equal" << std::endl;
}

Единственные недостатки, которые я вижу, - это более высокий расход памяти и необходимость в большем количестве строк кода. Но я попробую ваше решение.
— Knitschi

Я тоже сделал BaseEnum::iпубличный и BaseEnum::maxIприватный.
— Knitschi

Защищенный конструктор по умолчанию может быть проблемой, когда перечисление необходимо использовать в сторонних макросах или шаблонах, для которых требуется конструктор по умолчанию.
— Knitschi

3

Что ж, если вы определите enumс тем же именем в производном классе и запустите его с последнего элемента корреспондента enumв базовом классе, вы получите почти то, что хотите - унаследованное перечисление. Взгляните на этот код:

class Base
{
public:
    enum ErrorType
    {
        GeneralError,
        NoMemory,
        FileNotFound,
        LastItem,
    };
};

class Inherited: public Base
{
public:
    enum ErrorType
    {
        SocketError = Base::LastItem,
        NotEnoughBandwidth,
    };
};

1
пока код компилируемый, вы не сможете его использовать, так как компилятор не сможет преобразовать base :: ErrorType в Inherited :: ErrorType.
— bavaza

1
@bavaza, конечно, вы должны использовать целые числа вместо перечислений при передаче их значений в качестве параметров.
— Haspemulator 03

2

Как указано bayda, перечисления не имеют (и / или не должны) иметь функциональность, поэтому я применил следующий подход к вашему затруднительному положению, адаптировав Mykola Golubyevответ:

typedef struct
{
    enum
    {
        ONE = 1,
        TWO,
        LAST
    };
}BaseEnum;

typedef struct : public BaseEnum
{
    enum
    {
        THREE = BaseEnum::LAST,
        FOUR,
        FIVE
    };
}DerivedEnum;

2
Есть несколько проблем с этим решением. Во-первых, вы загрязняете BaseEnum LAST, который на самом деле не существует, кроме как установить отправную точку для DerivedEnum. Во-вторых, что, если я хочу явно установить некоторые значения в BaseEnum, которые будут конфликтовать со значениями DerivedEnum? В любом случае, это, вероятно, лучшее, что мы можем сделать в C ++ 14.
— Огњен Шобајић

Как я уже сказал, он адаптирован из предыдущего примера, поэтому он выражен так, как он есть для полноты, меня не беспокоит, что предыдущий плакат имеет логические проблемы в их примере
— бдительность

2

Вы можете использовать проект SuperEnum для создания расширяемых перечислений.

/*** my_enum.h ***/
class MyEnum: public SuperEnum<MyEnum>
{
public:
    MyEnum() {}
    explicit MyEnum(const int &value): SuperEnum(value) {}

    static const MyEnum element1;
    static const MyEnum element2;
    static const MyEnum element3;
};

/*** my_enum.cpp ***/
const MyEnum MyEnum::element1(1);
const MyEnum MyEnum::element2;
const MyEnum MyEnum::element3;

/*** my_enum2.h ***/
class MyEnum2: public MyEnum
{
public:
    MyEnum2() {}
    explicit MyEnum2(const int &value): MyEnum(value) {}

    static const MyEnum2 element4;
    static const MyEnum2 element5;
};

/*** my_enum2.cpp ***/
const MyEnum2 MyEnum2::element4;
const MyEnum2 MyEnum2::element5;

/*** main.cpp ***/
std::cout << MyEnum2::element3;
// Output: 3

1
Хотя это старый пост, я считаю, что он заслуживает ответа. Я бы предложил избавиться от конструктора по умолчанию и переместить явный конструктор в частный. Вы по-прежнему можете инициализировать переменную, как вы это делаете. Конечно, вы должны избавиться от const int&простогоint
— Moia

2

Вроде хакерство, но вот что я придумал, имея дело с перечислениями с областью видимости:

enum class OriginalType {
   FOO,  // 0
   BAR   // 1
   END   // 2
};

enum class ExtendOriginalType : std::underlying_type_t<OriginalType> {
   EXTENDED_FOO = static_cast<std::underlying_type_t<OriginalType>>
                                           (OriginalType::END), // 2
   EXTENDED_BAR  // 3
};

а затем используйте как:

OriginalType myOriginalType = (OriginalType)ExtendOriginalType::EXTENDED_BAR;

2

Этот ответ является вариантом ответа Брайана Р. Бонди. Поскольку был запрошен в комментарии, я добавляю его в качестве ответа. Я не говорю о том, действительно ли это того стоит.

#include <iostream>

class Colors
{
public:
    static Colors RED;
    static Colors GREEN;

    operator int(){ return value; }
    operator int() const{ return value; }

protected:
    Colors(int v) : value{v}{} 

private:
    int value;
};

Colors Colors::RED{1};
Colors Colors::GREEN{2};

class RGB : public Colors
{
public:
    static RGB BLUE;

private:
    RGB(int v) : Colors(v){}
};

RGB RGB::BLUE{10};

int main ()
{
  std::cout << Colors::RED << " " << RGB::RED << std::endl;
}

Жить в Колиру


0

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


-2
enum xx {
   ONE = 1,
   TWO,
   xx_Done
};

enum yy {
   THREE = xx_Done,
   FOUR,
};

typedef int myenum;

static map<myenum,string>& mymap() {
   static map<myenum,string> statmap;
   statmap[ONE] = "One";
   statmap[TWO] = "Two";
   statmap[THREE] = "Three";
   statmap[FOUR] = "Four";
   return statmap;
}

Применение:

std::string s1 = mamap()[ONE];
std::string s4 = mymap()[FOUR];
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.