基枚举类继承

在 C + + 中有没有一种模式可以从另一个枚举继承枚举?

差不多是这样:

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




enum eDerived: public eBase
{
four=4, five, six
};
97271 次浏览

Not possible. There is no inheritance with enums.

You can instead use classes with named const ints.

Example:

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

Impossible.
But you can define the enum anonymously in a class, then add additional enum constants in derived classes.

You can't do that directly, but you could try to use solution from this article.

The main idea is to use the helper template class which holds enum values and has the type cast operator. Considering that the underlying type for enum is int you can use this holder class seamlessly in your code instead of the enum.

Well, if you'll define enum with the same name in derived class and start it from last item of correspondent enum in base class, you'll receive almost what you want - inherited enum. Look at this code:

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


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

As stated by bayda, enum's don't (and/or shouldn't) have functionality, so I've taken the following approach to your quandary by adapting Mykola Golubyev's response:

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


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

Unfortunately it is not possible in C++14. I hope we will have such a language feature in C++17. As you already got few workarounds for your problem I won't provide a solution.

I would like to point out that the wording should be "extension" not "inheritance". The extension allows for more values (as you're jumping from 3 to 6 values in your example) whereas inheritance means putting more constraints to a given base class so the set of possibilities shrinks. Therefore, potential casting would work exactly opposite from inheritance. You can cast derived class to the base class and not vice-verse with class inheritance. But when having extensions you "should" be able to cast the base class to its extension and not vice-verse. I am saying "should" because, as I said such a language feature still doesn't exist.

How about this? Ok an instance is created for every possible value, but besides that its very flexible. Are there any downsides?

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

Usage:

BaseEnum e = DerivedEnum::THREE;


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

You can use a project SuperEnum to create extendable enumerations.

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

Usage:

std::string s1 = mymap()[ONE];
std::string s4 = mymap()[FOUR];

Kind of hacky but this is what I came up with if dealing with scoped enums:

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

and then use like:

OriginalType myOriginalType = (OriginalType)ExtendOriginalType::EXTENDED_BAR;

This answer is a variant of Brian R. Bondy answer. Since has been requested in a comment I'm adding it as answer. I'm not pointing about if it really worths though.

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

Live at Coliru

My Solution is similar to some above, except that I wanted to return in my functions like an enum (constructor that takes the STATUS_ENUM value), and compare like an enum (operators that compare the STATUS_ENUM value to the class). I also wanted a clean way of using the base class without having to cast and check things (operator override). Lastly I wanted to make sure that only the type I specify can construct the class (deleted template).

        struct StatusReturn
{
/**
* Use this to communicate trigger conditions internally to the caller.
* - Extend this class with a child who adds more static const STATUS_ENUM values as options.
* - When checking the return simply compare with != or == and the class will handle the rest.
*   - This is true for a base class and a derived value, since this base class holds the value.
*/


typedef int STATUS_ENUM;


StatusReturn() = delete;
            

template <typename T>
StatusReturn(T) = delete;
StatusReturn(STATUS_ENUM value): _value(value) {};


// Operator overloads to compare the int to the class
friend bool operator==(const StatusReturn & lhs, const STATUS_ENUM & rhs)
{ return lhs.getValue() == rhs; };
friend bool operator!=(const StatusReturn & lhs, const STATUS_ENUM & rhs)
{ return !(lhs == rhs); };
friend bool operator==(const STATUS_ENUM & lhs, const StatusReturn & rhs)
{ return lhs == rhs.getValue(); };
friend bool operator!=(const STATUS_ENUM & lhs, const StatusReturn & rhs)
{ return !(lhs == rhs); };


// Non-exit triggering return
static const STATUS_ENUM CONTINUE = -1;


// Exit triggering values
static const STATUS_ENUM FAILED = 0;
static const STATUS_ENUM SUCCESS = 1;
static const STATUS_ENUM HALTED = 2;


STATUS_ENUM getValue() const
{ return _value; };


protected:
STATUS_ENUM _value = CONTINUE;
};

Some examples of use:

        StatusReturn shouldExit()
{
return successBool ? StatusReturn::SUCCESS : StatusReturn::CONTINUE;
}

Which when called looks like:

        auto exitValue = shouldExit();
if (exitValue != StatusReturn::CONTINUE)
{
return exitValue;
}

Then a check of a derived class is as such:

        auto exitValue = shouldExit();
if (exitValue != DerivedReturn::DO_STUFF)
{
return exitValue;
}

Here, since DO_STUFF is also a STATUS_ENUM type, the operators just work without any explicit casting.