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

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

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

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


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

person Community    schedule 13.03.2009    source источник


Ответы (12)


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

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

Пример:

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;
};
person Brian R. Bondy    schedule 13.03.2009
comment
Есть ли проблемы с этим решением? Например, (у меня нет глубокого понимания полиморфизма). Можно ли иметь вектор‹Цвета› и использовать p = std::find (mycolors,mycolor+length,Colors::ORANGE);? - person jespestana; 12.06.2013
comment
@jespestana Нет, вы не будете использовать экземпляры класса Colors. Вы используете только значения int в статических членах const. - person jiandingzhe; 20.01.2015
comment
Если я правильно вас понимаю; тогда мне пришлось бы использовать контейнер vector‹Int›. Но я все равно мог бы написать: p = std::find (mycolors,mycolor+length,Colors::ORANGE);. правильно? - person jespestana; 21.01.2015
comment
@jespestana абсолютно. также, если поиск является очень распространенной операцией, рассмотрите возможность использования набора хэшей flat_set или открытого адреса. - person v.oddou; 22.06.2016
comment
Re: Есть ли какие-либо проблемы с этим решением? Может быть проблематично, что эти значения больше не относятся к разным типам. Вы не могли бы написать функцию, которая ожидает Color, как вы могли бы для enum. - person Drew Dormann; 15.10.2017
comment
@DrewDormann, если вместо static const int RED он становится static const Colors RED, инициализирующим переменную e, помещая значение int в приватное поле, он мог бы это сделать - person Moia; 26.07.2018
comment
@Moia - ваш комментарий был больше года назад, но есть ли шанс, что вы можете уточнить? если он делает static const Colors RED, то как называется окружающий класс? Я пытаюсь сделать что-то похожее на OP и задаюсь вопросом о вашем обходном пути. - person ; 12.12.2019
comment
@BithikaMookherjee я ответил прямо сейчас - person Moia; 13.12.2019
comment
Вам лучше сделать эти значения «constexpr», а не «const». - person Red.Wave; 11.03.2020

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

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

person Kirill V. Lyadvinsky    schedule 08.04.2010
comment
Хотя этот фрагмент кода может решить вопрос, включение объяснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос для будущих читателей, и эти люди могут не знать причин вашего предложения кода. - person NathanOliver; 12.02.2016
comment
Это отличный ответ; это один из тех случаев, когда проблема рассматривается по-другому, и идея использования шаблона действительно отвечает всем требованиям. - person Den-Jason; 11.08.2017
comment
Также взгляните на некоторые решения для этих шаблонов vis-a-vis: stackoverflow.com/questions/5871722/ - person Den-Jason; 11.08.2017

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

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

person Огњен Шобајић    schedule 18.09.2014
comment
Обратите внимание, что extends — это ключевое слово для наследования в языке Eiffel. - person Cheers and hth. - Alf; 28.06.2015
comment
Вы правы, потому что в этом случае принцип подстановки Лисков не соблюдается. Из-за этого комитет не примет решение, которое синтаксически выглядит как наследование. - person v.oddou; 22.06.2016

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

.h:

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;
}
person Dirk    schedule 26.10.2014
comment
Единственные недостатки, которые я вижу, - это более высокое потребление памяти и необходимость большего количества строк кода. Но я попробую ваше решение. - person Knitschi; 11.01.2017
comment
Я также сделал BaseEnum::i общедоступным и BaseEnum::maxI закрытым. - person Knitschi; 11.01.2017
comment
Защищенный конструктор по умолчанию может быть проблемой, когда перечисление необходимо использовать в сторонних макросах или шаблонах, для которых требуется конструктор по умолчанию. - person Knitschi; 12.01.2017

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

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

class Inherited: public Base
{
public:
    enum ErrorType
    {
        SocketError = Base::LastItem,
        NotEnoughBandwidth,
    };
};
person Haspemulator    schedule 29.06.2010
comment
пока код компилируется, вы не сможете его использовать, так как компилятор не сможет преобразовать base::ErrorType в Inherited::ErrorType. - person bavaza; 27.02.2011
comment
@bavaza, конечно, вы должны использовать целые числа вместо перечислений при передаче их значений в качестве параметров. - person Haspemulator; 03.03.2011

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

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

typedef struct : public BaseEnum
{
    enum
    {
        THREE = BaseEnum::LAST,
        FOUR,
        FIVE
    };
}DerivedEnum;
person vigilance    schedule 06.03.2013
comment
Есть несколько проблем с этим решением. Во-первых, вы загрязняете BaseEnum с помощью LAST, которого на самом деле не существует, кроме как установить начальную точку для DerivedEnum. Во-вторых, что, если я хочу явно установить некоторые значения в BaseEnum, которые будут конфликтовать со значениями DerivedEnum? В любом случае, это, вероятно, лучшее, что мы можем сделать в C++14. - person Огњен Шобајић; 19.09.2014
comment
Как я уже сказал, он адаптирован из предыдущего примера, поэтому он выражен так, как он есть, для полноты картины, меня не беспокоит, что предыдущий автор имеет логические проблемы в своем примере. - person vigilance; 04.06.2015

Вы можете использовать проект 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
person Dmitry Bravikov    schedule 05.01.2018
comment
Хотя это старый пост, я чувствую, что он заслуживает ответа. Я бы предложил избавиться от конструктора по умолчанию и переместить явный конструктор в частный. Вы все еще можете инициализировать переменную так, как делаете. Конечно, вы должны избавиться от const int& для простого int - person Moia; 26.07.2018

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

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;
person jsadler    schedule 08.02.2019

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

#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;
}

Жить в Coliru

person Moia    schedule 13.12.2019

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

person bayda    schedule 13.03.2009

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];
person Michal Cohen    schedule 10.01.2019

person    schedule
comment
Я смущен! Как бы вы тогда ссылались на типы Enum в аргументе переменной или функции и как бы вы гарантировали, что функция, ожидающая Enum, не получила EnumDeriv? - person Sideshow Bob; 29.11.2011
comment
Это не сработает. Когда вы определяете некоторые функции 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. Эти проблемы, вероятно, можно решить, добавив несколько операторов преобразования. - person SasQ; 12.12.2012