【问题标题】:Can a C++ enum class have methods?C++ 枚举类可以有方法吗?
【发布时间】:2014-02-13 06:41:16
【问题描述】:

我有一个包含两个值的枚举类,我想创建一个接收值的方法 并返回另一个。我还想维护类型安全(这就是我使用枚举类而不是枚举的原因)。

http://www.cplusplus.com/doc/tutorial/other_data_types/ 没有提及任何有关方法的内容 但是,我的印象是任何类型的类都可以有方法。

【问题讨论】:

  • 不,它不能。见here
  • @octavian 注意我的回答,请重新考虑您的用例!
  • @πάνταῥεῖ 你是完全正确的,我读过枚举但认为联合,杀死了评论。
  • @octavian 你甚至要求一个特定的用例,还是你只是想确认c++11enum class/struct的标准限制?跨度>
  • 我想到了一个用途......这是根本问题

标签: c++ methods enums


【解决方案1】:

不,他们不能。

我可以理解 C++11 中强类型枚举的 enum class 部分似乎暗示您的 enum 也具有 class 特征,但事实并非如此。我有根据的猜测是,关键字的选择受到了我们在 C++11 之前用于获取作用域枚举的模式的启发:

class Foo {
public:
  enum {BAR, BAZ};
};

但是,这只是语法。同样,enum class 不是 class

【讨论】:

  • 在##C++ 上,有人告诉我“c++ 的目标是尽可能地让人困惑和对专家友好”。显然这是个玩笑,但你明白了:)
  • union 也不是 John Doe 认为的 class。然而它们可以有成员函数。并且类对于成员函数实际上不是强制性的。使用valuethis 之类的指示符,enum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; } 之类的东西(这里也允许;),它与其他形式的函数一样有意义。
  • 为什么不是struct Foo{ enum {BAR, BAZ}; };,所以不是enum struct
  • 哦,enum struct 实际上是创建作用域枚举的有效方法!谁知道!
【解决方案2】:

虽然“您不能”的答案在技术上是正确的,但我相信您可以使用以下想法实现您正在寻找的行为:

我想你想写这样的东西:

Fruit f = Fruit::Strawberry;
f.IsYellow();

你希望代码看起来像这样:

enum class Fruit : uint8_t
{
  Apple, 
  Pear,
  Banana,
  Strawberry,

  bool IsYellow() { return this == Banana; }
};

但当然,它不起作用,因为枚举不能有方法(并且'this'在上述上下文中没有任何意义)

但是,如果您使用包含非类枚举和包含该类型值的单个成员变量的普通类的想法,您可以非常接近您想要的语法/行为/类型安全。即:

class Fruit
{
public:
  enum Value : uint8_t
  {
    Apple,
    Pear,
    Banana,
    Strawberry
  };

  Fruit() = default;
  constexpr Fruit(Value aFruit) : value(aFruit) { }

#if Enable switch(fruit) use case:
  // Allow switch and comparisons.
  constexpr operator Value() const { return value; }

  // Prevent usage: if(fruit)
  explicit operator bool() = delete;        
#else
  constexpr bool operator==(Fruit a) const { return value == a.value; }
  constexpr bool operator!=(Fruit a) const { return value != a.value; }
#endif

  constexpr bool IsYellow() const { return value == Banana; }

private:
  Value value;
};

现在你可以写了:

Fruit f = Fruit::Strawberry;
f.IsYellow();

编译器会阻止这样的事情:

Fruit f = 1;  // Compile time error.

您可以轻松添加如下方法:

Fruit f("Apple");

f.ToString();

可以支持。

【讨论】:

  • 不应该也是IsYellow(), operator==, != 标记为constexpr吗?
  • 我收到“错误:在令牌“switch”之前缺少二元运算符“
  • 这真的很聪明。有没有办法让类中的枚举也成为enum class
  • 能否详细说明一下宏 if-else 部分?
  • 您的先生,这是迄今为止最好的答案。
【解决方案3】:

专注于问题的描述而不是标题,可能的答案是

struct LowLevelMouseEvent {
    enum Enum {
        mouse_event_uninitialized = -2000000000, // generate crash if try to use it uninitialized.
        mouse_event_unknown = 0,
        mouse_event_unimplemented,
        mouse_event_unnecessary,
        mouse_event_move,
        mouse_event_left_down,
        mouse_event_left_up,
        mouse_event_right_down,
        mouse_event_right_up,
        mouse_event_middle_down,
        mouse_event_middle_up,
        mouse_event_wheel
    };
    static const char* ToStr (const type::LowLevelMouseEvent::Enum& event)
    {
        switch (event) {
            case mouse_event_unknown:         return "unknown";
            case mouse_event_unimplemented:   return "unimplemented";
            case mouse_event_unnecessary:     return "unnecessary";
            case mouse_event_move:            return "move";
            case mouse_event_left_down:       return "left down";
            case mouse_event_left_up:         return "left up";
            case mouse_event_right_down:      return "right down";
            case mouse_event_right_up:        return "right up";
            case mouse_event_middle_down:     return "middle down";
            case mouse_event_middle_up:       return "middle up";
            case mouse_event_wheel:           return "wheel";
            default:
                Assert (false);
                break;
        }
        return "";
    }
};

【讨论】:

    【解决方案4】:

    other answer 中所述,没有。甚至enum class 也不是一个类。


    通常需要拥有enum的方法是因为它不是常规(只是递增)枚举,而是值的按位定义被屏蔽或需要其他位算术运算:

    enum class Flags : unsigned char {
        Flag1 = 0x01 , // Bit #0
        Flag2 = 0x02 , // Bit #1
        Flag3 = 0x04 , // Bit #3
        // aso ...
    }
    
    // Sets both lower bits
    unsigned char flags = (unsigned char)(Flags::Flag1 | Flags::Flag2);
    
    // Set Flag3
    flags |= Flags::Flag3;
    
    // Reset Flag2
    flags &= ~Flags::Flag2;
    

    显然,人们考虑封装必要的操作来重新/设置单个/一组位,例如位掩码值甚至位索引驱动的操作对于操作这样一组“标志”很有用。

    struct/class specification 只支持更好地限定枚举值以进行访问。不多不少!

    摆脱不能为枚举(类)声明方法的限制的方法是,使用std::bitset(包装类)或bitfield union

    unions,这样的位域联合可以有方法(限制见here!)。

    我有一个示例,如何将位掩码值(如上所示)转换为相应的位索引,可以在此处与 std::bitset 一起使用:BitIndexConverter.hpp
    我发现这对于增强某些基于“标志”决策的算法的可读性非常有用。

    【讨论】:

    • 还有更多的用例需要枚举类的方法,例如toString() 和 fromString()。每种(甚至不是)现代主要语言都有这个(例如 C#、Java、Swift),只是没有 C++。
    【解决方案5】:

    有一种相当兼容的能力(§)可以将枚举重构为一个类,而无需重写代码,这意味着您可以有效地做自己的事情要求不要进行过多的编辑。

    (§) 正如 ElementW 在评论中指出的那样,依赖于 type_traits 的代码将不起作用,例如不能用auto之类的。可能有一些处理这些东西的方法,但最终是把一个枚举转换成一个类,而颠覆C++总是错误的

    enum structenum class 规范是关于范围的,因此不是其中的一部分。

    您的原始枚举是例如'pet'(这仅作为示例!)。

    enum pet { 
        fish, cat, dog, bird, rabbit, other 
    };
    

    (1) 您将其修改为例如 petEnum(以便将其隐藏在您现有的代码中)。

    enum petEnum { 
        fish, cat, dog, bird, rabbit, other 
    };
    

    (2) 在它下面添加一个新的类声明(以原始枚举命名)

    class pet {
        private:
            petEnum value;
            pet() {}
    
        public:
            pet(const petEnum& v) : value{v} {} //not explicit here.
            operator petEnum() const { return value; }
            pet& operator=(petEnum v) { value = v; return *this;}
            bool operator==(const petEnum v) const { return value == v; }
            bool operator!=(const petEnum v) const { return value != v; }
     //     operator std::string() const;
    
    };
    

    (3) 您现在可以将任何您喜欢的类方法添加到您的宠物类中。 例如。字符串运算符

        pet::operator std::string() const {
            switch (value) {
                case fish: return "fish";
                case cat:  return "cat";
                case dog:  return "dog";
                case bird: return "bird";
                case rabbit: return "rabbit";
                case other: return "Wow. How exotic of you!";
            }
        }
    

    现在你可以使用例如 std::cout...

    int main() {
        pet myPet = rabbit;
        if(myPet != fish) {
            cout << "No splashing! ";
        }
        std::cout << "I have a " << std::string(myPet) << std::endl;
        return 0;
    }
    

    【讨论】:

    • 完全兼容:如果您将枚举值与任何类型的类型推导一起使用,并且预计会获得pet 类型名/实例,则可以是模板、autodecltype,这会中断,因为您会得到 petEnum
    【解决方案6】:

    它可能无法满足您的所有需求,但与非会员运营商一起,您仍然可以享受很多乐趣。例如:

    #include <iostream>
    
    enum class security_level
    {
        none, low, medium, high
    };
    
    static bool operator!(security_level s) { return s == security_level::none; }
    
    static security_level& operator++(security_level& s)
    {
        switch(s)
        {
            case security_level::none: s = security_level::low; break;
            case security_level::low: s = security_level::medium; break;
            case security_level::medium: s = security_level::high; break;
            case security_level::high: break;
        }
        return s;
    }
    
    static std::ostream & operator<<(std::ostream &o, security_level s)
    {
        switch(s)
        {
            case security_level::none: return o << "none";
            case security_level::low: return o << "low";
            case security_level::medium: return o << "medium";
            case security_level::high: return o << "high";
        }
    }
    

    这允许像这样的代码

    security_level l = security_level::none;   
    if(!!l) { std::cout << "has a security level: " << l << std::endl; } // not reached
    ++++l;
    if(!!l) { std::cout << "has a security level: " << l << std::endl; } // reached: "medium"
    

    【讨论】:

      【解决方案7】:

      基于jtlim's answer

      想法(解决方案)

      enum ErrorType: int {
        noConnection,
        noMemory
      };
      
      class Error {
      public:
        Error() = default;
        constexpr Error(ErrorType type) : type(type) { }
      
        operator ErrorType() const { return type; }
        constexpr bool operator == (Error error) const { return type == error.type; }
        constexpr bool operator != (Error error) const { return type != error.type; }    
        constexpr bool operator == (ErrorType errorType) const { return type == errorType; }
        constexpr bool operator != (ErrorType errorType) const { return type != errorType; }
      
        String description() { 
          switch (type) {
          case noConnection: return "no connection";
          case noMemory: return "no memory";
          default: return "undefined error";
          }
       }
      
      private:
        ErrorType type;
      };
      

      用法

      Error err = Error(noConnection);
      err = noMemory;
      print("1 " + err.description());
      
      switch (err) {
        case noConnection: 
          print("2 bad connection");
          break;
        case noMemory:
          print("2 disk is full");
          break;
        default: 
          print("2 oops");
          break;
      }
      
      if (err == noMemory) { print("3 Errors match"); }
      if (err != noConnection) { print("4 Errors don't match"); }
      

      【讨论】:

        猜你喜欢
        • 2013-05-16
        • 2011-11-16
        • 2015-11-29
        • 1970-01-01
        • 1970-01-01
        • 2010-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多