在 C++ 中扩展枚举?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1804840/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:11:52  来源:igfitidea点击:

Extending enums in C++?

c++enums

提问by cvb

Is there a way in C++ to extend/"inherit" enums?

C++ 中有没有办法扩展/“继承”枚举?

I.E:

IE:

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

or at least define a conversion between them?

或者至少定义它们之间的转换?

采纳答案by Matthieu M.

No, there is not.

不,那里没有。

enumare really the poor thing in C++, and that's unfortunate of course.

enum在 C++ 中真的很糟糕,这当然是不幸的。

Even the class enumintroduced in C++0x does not address this extensibility issue (though they do some things for type safety at least).

即使class enum在 C++0x 中引入的也没有解决这个可扩展性问题(尽管它们至少为类型安全做了一些事情)。

The only advantage of enumis that they do not exist: they offer some type safety while not imposing any runtime overhead as they are substituted by the compiler directly.

唯一的优点enum是它们不存在:它们提供了一些类型安全性,同时不强加任何运行时开销,因为它们被编译器直接替换。

If you want such a beast, you'll have to work yourself:

如果你想要这样的野兽,你必须自己工作:

  • create a class MyEnum, that contains an int (basically)
  • create named constructors for each of the interesting values
  • 创建一个MyEnum包含 int的类(基本上)
  • 为每个有趣的值创建命名构造函数

you may now extend your class (adding named constructors) at will...

您现在可以随意扩展您的类(添加命名构造函数)...

That's a workaround though, I have never found a satistifying way of dealing with an enumeration...

不过,这是一种解决方法,我从来没有找到一种令人满意的处理枚举的方法......

回答by marco

I've solved in this way:

我是这样解决的:

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

Of course, if you add a new net protocol in the NetProtocols.def file, you have to recompile, but at least it's expandable.

当然,如果在 NetProtocols.def 文件中添加新的网络协议,则必须重新编译,但至少它是可扩展的。

回答by Laurence Gonsalves

If you were able to create a subclass of an enum it'd have to work the other way around.

如果您能够创建枚举的子类,则必须以相反的方式工作。

The set of instances in a sub-class is a subsetof the instances in the super-class. Think about the standard "Shape" example. The Shape class represents the set of all Shapes. The Circle class, its subclass, represents the subset of Shapes that are Circles.

子类中的实例是超类中实例的子集。想想标准的“形状”示例。Shape 类表示所有形状的集合。Circle 类是它的子类,它代表圆形的 Shapes 子集。

So to be consistent, a subclass of an enum would have to contain a subset of the elements in the enum it inherits from.

因此,为了保持一致,枚举的子类必须包含它继承的枚举中元素的子集。

(And no, C++ doesn't support this.)

(不,C++ 不支持这一点。)

回答by Marat

A simple, but useful workaround for this c++ gap could be as follows:

解决此 c++ 差距的简单但有用的解决方法如下:

#define ENUM_BASE_VALS A,B,C
enum Enum {ENUM_BASE_VALS};
enum EnumEx {ENUM_BASE_VALS, D,E,F};

回答by BadPirate

http://www.codeproject.com/KB/cpp/InheritEnum.aspxgoes over a method to created an expanded enum.

http://www.codeproject.com/KB/cpp/InheritEnum.aspx 介绍了一种创建扩展枚举的方法。

回答by elatalhm

Just an idea:

只是一个想法:

You could try to create an empty class for each constant (maybe put them all in the same file to reduce clutter), create one instance of each class and use the pointers to these instances as the "constants". That way, the compiler will understand inheritance and will perform any ChildPointer-to-ParentPointer conversion necessary when using function calls, AND you still get type-safety checks by the compiler to ensure no one passes an invalid int value to functions (which would have to be used if you use the LAST value method to "extend" the enum).

您可以尝试为每个常量创建一个空类(可能将它们全部放在同一个文件中以减少混乱),为每个类创建一个实例并将指向这些实例的指针用作“常量”。这样,编译器将理解继承并在使用函数调用时执行任何必要的 ChildPointer-to-ParentPointer 转换,并且您仍然会得到编译器的类型安全检查以确保没有人将无效的 int 值传递给函数(这将有如果您使用 LAST 值方法来“扩展”枚举,将使用)。

Haven't fully thought this through though so any comments on this approach are welcome.

不过还没有完全考虑清楚,因此欢迎对这种方法提出任何意见。

And I'll try to post an example of what I mean as soon as I have some time.

我会尽快发布一个示例来说明我的意思。

回答by Andrew Goedhart

I had this problem in some projects that ran on small hardware devices I design. There is a common project that holds a number of services. Some of these services use enums as parameters to get additional type checking and safety. I needed to be able to extend these enums in the projects that use these services.

我在一些运行在我设计的小型硬件设备上的项目中遇到了这个问题。有一个共同的项目,其中包含许多服务。其中一些服务使用枚举作为参数来获得额外的类型检查和安全性。我需要能够在使用这些服务的项目中扩展这些枚举。

As other people have mentioned c++ doesn't allow you to extend enums. You can however emulate enums using a namespace and a template that has all the benefits of enum class.

正如其他人所提到的,c++ 不允许您扩展枚举。但是,您可以使用命名空间和具有enum class 的所有优点的模板来模拟enum

enum classhas the following benefits:

枚举类有以下好处:

  1. Converts to a known integer type.
  2. Is a value type
  3. Is constexpr by default and takes up no valuable RAM on small processors
  4. Is scoped and accessible by enum::value
  5. Works in case statements
  6. Provides type safety when used as a parameter and needs to be explicitly cast
  1. 转换为已知整数类型。
  2. 是值类型
  3. 默认情况下是 constexpr 并且在小型处理器上不占用宝贵的 RAM
  4. 由 enum::value 限定和访问
  5. 适用于 case 语句
  6. 当用作参数并需要显式转换时提供类型安全

Now if you define a class as an enumyou can't create constexprinstances of the enumin the class declaration, because the class is not yet complete and it leads to a compile error. Also even if this worked you could not extend the value set of enums easily later in another file/sub project .

现在,如果您将类定义为枚举,则无法在类声明中创建该枚举的constexpr实例,因为该类尚未完成并且会导致编译错误。此外,即使这有效,您也无法稍后在另一个文件/子项目中轻松扩展枚举的值集。

Now namespaceshave no such problem but they don't provide type safety.

现在命名空间没有这样的问题,但它们不提供类型安全。

The answer is to first create a templated base class which allows enums of different base sizes so we don't waste what we don't use.

答案是首先创建一个模板化的基类,它允许不同基本大小的枚举,这样我们就不会浪费我们不使用的东西。

template <typename TYPE>
class EnumClass {
  private:
    TYPE value_;
  public:
    explicit constexpr EnumClass(TYPE value) :
        value_(value){
    }
    constexpr EnumClass() = default;
    ~EnumClass() = default;
    constexpr explicit EnumClass(const EnumClass &) = default;
    constexpr EnumClass &operator=(const EnumClass &) = default;

    constexpr operator TYPE() const {return    value_;}
    constexpr TYPE value() const {return value_;}

};

Then for each enum classwe want to extend and emulate we create a namespace and a Type like this:

然后对于我们想要扩展和模拟的每个枚举类,我们创建一个命名空间和一个类型,如下所示:

namespace EnumName {
   class Type :public Enum<uint8_t> {
     public:
        explicit constexpr Type(uint8_t value): Enum<uint8_t>(value){}
        constexpr Enum() = default;
   }
   constexpr auto Value1 = Type(1); 
   constexpr auto Value2 = Type(2); 
   constexpr auto Value3 = Type(3); 
}

Then later in your code if you have included the original EnumName you can do this:

然后在你的代码中,如果你已经包含了原始的 EnumName 你可以这样做:

   namespace EnumName {
       constexpr auto Value4 = Type(4U); 
       constexpr auto Value5 = Type(5U); 
       constexpr auto Value6 = Type(6U); 

       constexpr std::array<Type, 6U> Set = {Value1, Value2, Value3, Value4, Value5, Value6};
    }

now you can use the Enumlike this:

现在你可以 像这样使用枚举

#include <iostream>

void fn(EnumName::Type val){
    if( val != EnumName::Value1 ){
      std::cout << val;
    }
}

int main(){
  for( auto e :EnumName::Set){
    switch(e){
      case EnumName::Value1:  
        std::cout << "a";
        break;
      case EnumName::Value4:  
        std::cout << "b";
        break;
      default:
        fn(e);
    }
  }
}

So we have a case statement, enum comparisons, parameter type safety and its all extensible. Note the set is constexprand wont end up using valuable RAM on a small micro (placement verified on Godbolt.org. :-). As a bonus we have the ability to iterate over a set of enum values.

所以我们有一个 case 语句、枚举比较、参数类型安全和所有可扩展的。请注意,该集合是constexpr,最终不会在小型微型计算机上使用宝贵的 RAM(在 Godbolt.org 上验证了位置。:-)。作为奖励,我们可以迭代一组枚举值。

回答by Andrew Goedhart

Actually you can extend enums in a round about way.

实际上,您可以全面扩展枚举。

The C++ standard defines the valid enum values to be all the valid values of the underlying type so the following is valid C++ (11+). Its not Undefined Behaviour, but it is very nasty - you have been warned.

C++ 标准将有效枚举值定义为基础类型的所有有效值,因此以下是有效的 C++ (11+)。它不是未定义的行为,但它非常讨厌——你已经被警告过。

#include <cstdint>

    enum Test1:unit8_t {
        Value1 =0,
        Value2 =1
    };

    constexpr auto Value3 = static_cast<Test1>(3);
    constexpr auto Value4 = static_cast<Test1>(4);
    constexpr auto Value5 = static_cast<Test1>(5);


Test1 fn(Test1 val){
  switch(val){
    case Value1:
    case Value2:
    case Value3:
    case Value4:
       return Value1;
    case Value5:
       return Value5; 
  } 
}

int main(){
  return static_cast<uint8_t>(fn(Value5));
} 

Note that most of the compilers don't consider the additional values as part of the set for generating warnings about missing enums values in switch statements.So clang and gcc will warn if Value2 is missing but will do nothing if Value4 is missing in the above switch statement.

请注意,大多数编译器不考虑将附加值作为生成有关 switch 语句中缺少枚举值的警告的集合的一部分。因此,如果缺少 Value2,clang 和 gcc 将发出警告,但如果上面缺少 Value4,则不会执行任何操作开关语句。

回答by Dave Harris

I do this '

我这样做'

'''
    enum OPC_t // frame Operation Codes
    {
      OPC_CVSND = 0 // Send CV value
    , OPC_CVREQ = 1 // Request CV (only valid for master app)
    , OPC_COMND = 2 // Command
    , OPC_HRTBT = 3 // Heart Beat
    };
    enum rxStatus_t     // this extends OPC_t
    {
      RX_CVSND = OPC_CVSND  // Send CV value
    , RX_CVREQ = OPC_CVREQ  // Request CV
    , RX_COMND = OPC_COMND  // Command
    , RX_HRTBT = OPC_HRTBT  // Heart Beat
    , RX_NONE       // No new Rx
    , RX_NEWCHIP        // new chip detected
    };
''''

回答by Alexey Malistov

The following code works well.

以下代码运行良好。

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