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

ddrv8njm  于 2022-12-24  发布在  其他
关注(0)|答案(8)|浏览(172)

我有一个有两个值的枚举类,我想创建一个接收一个值并返回另一个值的方法。我还想维护类型安全(这就是为什么我使用枚举类而不是枚举)。
http://www.cplusplus.com/doc/tutorial/other_data_types/没有提到任何关于方法的内容,然而,我的印象是任何类型的类都可以有方法。

zpjtge22

zpjtge221#

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

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

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

mcvgt66p

mcvgt66p2#

虽然“你做不到”的答案在技术上是正确的,但我相信你可以通过以下方法实现你想要的行为:
我想你会想写这样的东西:

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() const = 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();

可以被支撑。

92vpleto

92vpleto3#

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

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

ncgqoxb04#

有一个非常兼容的能力(§)可以将枚举重构为类,而不必重写代码,这意味着有效地,您可以做您要求做的事情,而无需过多编辑。
(§)* 正如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;
}
9rbhqvlz

9rbhqvlz5#

正如在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;

显然,考虑通过例如位掩码值或甚至位索引驱动的操作来封装重置/设置单个/组位所必需的操作对于操纵这样一组“标志”将是有用的。
c++11struct/classspecification只是支持更好的枚举值的作用域来访问。
摆脱不能为枚举(类)声明方法的限制的方法是,要么使用std::bitset( Package 类),要么使用bitfield union
union s,并且这样的位域联合
可以
具有方法(有关限制,请参见here!)。
我有一个示例,如何将位掩码值(如上所示)转换为相应的位索引,可以在这里与std::bitset一起使用:BitIndexConverter.hpp
我发现这对于增强一些基于“标志”决策的算法的可读性非常有用。

nzk0hqpo

nzk0hqpo6#

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

#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"
yb3bgrhw

yb3bgrhw7#

基于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"); }
t2a7ltrp

t2a7ltrp8#

是的,它们可以,但是您需要创建一个 Package 类,例如:

#include <iostream>
using namespace std;

class Selection {
    public: 
        enum SelectionEnum {
            yes,
            maybe,
            iDontKnow,
            canYouRepeatTheQuestion
        };
        Selection(SelectionEnum selection){value=selection;};
        string toString() {
            string selectionToString[4]={
                "Yes",
                "Maybe",
                "I don't know",
                "Can you repeat the question?"
            };
            return selectionToString[value];
        };
    private:
        SelectionEnum value;
};

int main(){
    Selection s=Selection(Selection::yes);
    cout<<s.toString()<<endl;
    return 0;
}

相关问题