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


79

Чи існує шаблон, де я можу успадкувати enum від іншого переліку в 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 лише у статичних членах const.
jiandingzhe

Якщо я вас правильно розумію; тоді мені довелося б використовувати векторний контейнер <Int>. Але я все одно міг би написати: p = std :: find (mycolors, mycolor + length, Colors :: ORANGE) ;. так?
єспестана

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

1
Re: Чи є якісь проблеми з цим рішенням? Може бути проблематично, що ці значення більше не мають різного типу. Ви не можете написати функцію, яка очікує на Color, як ви могли б на enum.
Drew Dormann

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?
Сайд-шоу Боб

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

Ви не можете цього зробити безпосередньо, але спробуйте використати рішення з цієї статті.

Основна ідея полягає у використанні допоміжного класу шаблону, який містить значення перерахування та має оператор приведення типу. Враховуючи, що основним типом переліку є те, що intви можете використовувати цей клас власника безперешкодно у своєму коді замість переліку.


Незважаючи на те, що цей фрагмент коду може вирішити питання, включення пояснення дійсно допомагає поліпшити якість вашої публікації. Пам’ятайте, що ви будете відповідати на запитання для читачів у майбутньому, і ці люди можуть не знати причин вашої пропозиції коду.
NathanOliver

Це чудова відповідь; це один із тих випадків, коли "подумай про проблему по-іншому", і ідея використання шаблону насправді відповідає цілому.
Ден-Джейсон,

Також погляньте на деякі рішення цих візаві шаблонів: stackoverflow.com/questions/5871722/…
Ден-Джейсон,

5

На жаль, це неможливо в C ++ 14. Сподіваюся, у нас буде така мовна функція в C ++ 17. Оскільки у вас вже є кілька обхідних шляхів вирішення вашої проблеми, я не буду надавати рішення.

Я хотів би зазначити, що формулювання повинно бути "продовження", а не "спадкування". Розширення дозволяє отримати більше значень (оскільки ви переходите від 3 до 6 значень у вашому прикладі), тоді як успадкування означає введення більшої кількості обмежень для даного базового класу, щоб набір можливостей скорочувався. Тому потенційний кастинг працював би прямо навпаки від успадкування. Ви можете передати похідний клас до базового класу, а не до вірша зі спадкуванням класу. Але, маючи розширення, ви "повинні" мати можливість передати базовий клас на його розширення, а не навпаки. Я кажу "повинен", оскільки, як я вже говорив, така функція мови все ще не існує.


Зверніть увагу, що extendsце ключове слово для успадкування на мові Ейфеля.
Вітаю і hth. - Альф

Ви маєте рацію, оскільки в цьому випадку Принцип заміщення Ліскова не дотримується. Комітет не прийме рішення, яке синтаксично виглядає як успадкування.
v.oddou

4

Як щодо цього? Добре, екземпляр створюється для кожного можливого значення, але крім цього дуже гнучкий. Чи є мінуси?

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

Єдиними недоліками, які я бачу, є більший обсяг пам'яті та необхідність більшої кількості рядків коду. Але я спробую ваше рішення.
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, звичайно, ви повинні використовувати ціле число замість перелічень, передаючи їх значення як параметри.
Хаспемулятор 03.03.11

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
Моя

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

Прямий ефір у Coliru


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.