C++枚举类可以有方法吗?

oct*_*ian 117 c++ methods enums

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

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

Ste*_*ppo 100

不,他们不能.

我可以理解,enum classC++ 11中强类型枚举的部分似乎暗示你enum也具有class特征,但实际情况并非如此.我的有根据的猜测是,关键字的选择受到我们在C++ 11之前使用的模式的启发,以获得范围内的枚举:

class Foo {
public:
  enum {BAR, BAZ};
};
Run Code Online (Sandbox Code Playgroud)

但是,这只是语法.再说一遍,enum class不是class.

  • 在## C++上,我被告知_"c ++旨在尽可能让人感到困惑和友好"_.显然这是个玩笑,但你明白了:) (75认同)
  • 一个`union`也不是John Doe所认为的_class_.但他们可以拥有成员职能.对于成员函数,类实际上不是强制性的.使用像'value`或`this`这样的指示符,比如`enum Size {Huge,Mega,Apocalypse; bool运算符<(X rhs)const {return*this <rhs; }`(这里也允许`;`),它可以像其他形式的函数一样有意义. (4认同)
  • 哦,“enum struct”实际上是创建作用域枚举的有效方法!谁知道! (3认同)
  • 为什么不是 `struct Foo{ enum {BAR, BAZ}; };` 因此是 `enum struct`? (2认同)

jtl*_*lim 52

虽然回答说:"你不能"在技术上是正确的,我相信你可以实现你正在寻找使用以下想法的行为:

我想你想写的东西如下:

Fruit f = Fruit::Strawberry;
f.IsYellow();
Run Code Online (Sandbox Code Playgroud)

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

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

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

...
Run Code Online (Sandbox Code Playgroud)

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

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

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:
  operator Value() const { return value; }  // Allow switch and comparisons.
                                            // note: Putting constexpr here causes
                                            // clang to stop warning on incomplete
                                            // case handling.
  explicit operator bool() = delete;        // Prevent usage: if(fruit)
#else
  constexpr operator==(Fruit a) const { return value == a.value; }
  constexpr operator!=(Fruit a) const { return value != a.value; }
#endif

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

private:
  Value value;
};
Run Code Online (Sandbox Code Playgroud)

现在你可以写:

Fruit f = Fruit::Strawberry;
f.IsYellow();
Run Code Online (Sandbox Code Playgroud)

并且编译器会阻止以下内容:

Fruit f = 1;  // Compile time error.
Run Code Online (Sandbox Code Playgroud)

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

Fruit f("Apple");
Run Code Online (Sandbox Code Playgroud)

f.ToString();
Run Code Online (Sandbox Code Playgroud)

可以支持.

  • 不应该也将 IsYellow(), operator==, != 标记为 constexpr 吗? (2认同)
  • 我收到“错误:令牌“switch”之前缺少二元运算符” (2认同)
  • 您能详细说明一下宏 if-else 部分吗? (2认同)

小智 17

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

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 "";
    }
};
Run Code Online (Sandbox Code Playgroud)


πάν*_*ῥεῖ 5

正如另一个答案中提到的,没有。甚至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;
Run Code Online (Sandbox Code Playgroud)

显然,有人认为封装必要的操作以重新/设置单个/一组位,例如通过位掩码值或什至位索引驱动的操作对于操纵这样一组“标志”是有用的。

struct /class 规范只支持用于接入枚举值的更好的范围界定。不多也不少!

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

unions,并且这样的位域联合可以有方法(有关限制,请参见此处!)。

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

  • 有更多用例需要枚举类上的方法,例如 toString() 和 fromString()。每个(甚至不是这样)现代主要语言都有这个(例如 C#、Java、Swift),只是没有 C++。 (38认同)

Kon*_*hog 5

有一种非常兼容的能力(§) 可以将枚举重构为类,而无需重写代码,这意味着您可以有效地完成您要求做的事情而无需进行太多编辑。

(§)正如 ElementW 在评论中指出的那样,依赖于type_traits 的代码将不起作用,因此例如不能使用 auto 等。可能有某种方法可以处理这些东西,但最终将枚举转换为类,而且颠覆C++永远是错误的

enum structenum class规格约作用域所以没有这部分。

您的原始枚举是例如“宠物”(这仅作为示例!)。

enum pet { 
    fish, cat, dog, bird, rabbit, other 
};
Run Code Online (Sandbox Code Playgroud)

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

enum petEnum { 
    fish, cat, dog, bird, rabbit, other 
};
Run Code Online (Sandbox Code Playgroud)

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

};
Run Code Online (Sandbox Code Playgroud)

(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!";
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在你可以使用例如 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 它 ** 不** 完全兼容:如果您将枚举值与任何类型的类型推导一起使用,并且期望获得 `pet` 类型名/实例,无论是模板、`auto` 还是 `decltype`,这中断,因为你得到了一个 `petEnum`。 (2认同)