Một lớp enum C ++ có thể có các phương thức không?


144

Tôi có một lớp enum với hai giá trị và tôi muốn tạo một phương thức nhận giá trị và trả về giá trị kia. Tôi cũng muốn duy trì loại an toàn (đó là lý do tại sao tôi sử dụng lớp enum thay vì enum).

http://www.cplusplus.com/doc/tutorial/other_data_types/ không đề cập bất cứ điều gì về các phương thức Tuy nhiên, tôi có ấn tượng rằng bất kỳ loại lớp nào cũng có thể có các phương thức.


4
Không, nó không thể. Xem ở đây .
juanchopanza

@octavian Lưu ý câu trả lời của tôi và suy nghĩ lại về các trường hợp sử dụng của bạn xin vui lòng!
πάντα ῥεῖ

@ πάτῥεῖ bạn hoàn toàn đúng, tôi đã đọc enum nhưng nghĩ rằng liên minh, đã giết bình luận.
Eugen Constantin Dinca

@octavian bạn thậm chí yêu cầu cho một trường hợp sử dụng cụ thể nào cả, hoặc đã làm bạn chỉ muốn có các tiêu chuẩn hạn chế về c ++ 11 enum class/struct khẳng định?
πάντα ῥεῖ

Tôi đã sử dụng trong tâm trí ... và đây là vấn đề cơ bản
octavian

Câu trả lời:


118

Không, họ không thể.

Tôi có thể hiểu rằng enum classphần dành cho các enum được gõ mạnh trong C ++ 11 dường như ngụ ý rằng bạn enumcũng có classnhững đặc điểm, nhưng thực tế không phải vậy. Dự đoán có học thức của tôi là sự lựa chọn của các từ khóa được lấy cảm hứng từ mẫu chúng tôi đã sử dụng trước C ++ 11 để có được các danh sách có phạm vi:

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

Tuy nhiên, đó chỉ là cú pháp. Một lần nữa, enum classkhông phải là một class.


88
Trên ## C ++, tôi được thông báo rằng "c ++ nhằm mục đích trở nên khó hiểu và thân thiện với chuyên gia nhất có thể" . Rõ ràng đó là một trò đùa, nhưng bạn có ý tưởng :)
Stefano Sanfilippo

4
A unionkhông phải là những gì John Doe cũng sẽ xem xét một lớp học . Tuy nhiên, họ có thể có chức năng thành viên. Và các lớp học thực sự không bắt buộc cho các chức năng thành viên. Sử dụng một chỉ định như valuehoặc this, một cái gì đó như enum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; }(ở đây cũng cho phép ;), nó có thể có ý nghĩa tương tự như các dạng hàm khác.
Sebastian Mach

84

Mặc dù câu trả lời là "bạn không thể" là đúng về mặt kỹ thuật, tôi tin rằng bạn có thể đạt được hành vi mà bạn đang tìm kiếm bằng cách sử dụng ý tưởng sau:

Tôi tưởng tượng rằng bạn muốn viết một cái gì đó như:

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

Và bạn đã hy vọng rằng mã trông giống như thế này:

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

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

...

Nhưng tất nhiên, nó không hoạt động, bởi vì enums không thể có phương thức (và 'điều này' không có nghĩa gì trong bối cảnh trên)

Tuy nhiên, nếu bạn sử dụng ý tưởng về một lớp bình thường chứa một enum không phải lớp và một biến thành viên duy nhất có chứa một giá trị của kiểu đó, bạn có thể cực kỳ gần với sự an toàn của cú pháp / hành vi / kiểu mà bạn muốn. I E:

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

Bây giờ bạn có thể viết:

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

Và trình biên dịch sẽ ngăn chặn những thứ như:

Fruit f = 1;  // Compile time error.

Bạn có thể dễ dàng thêm các phương thức sao cho:

Fruit f("Apple");

f.ToString();

có thể được hỗ trợ.


1
Không nên là IsYellow (), toán tử == ,! = Được đánh dấu là constexpr?
Jarek C

Tôi đang gặp "lỗi: thiếu toán tử nhị phân trước khi chuyển đổi" token "
Pedro77

18

Tập trung vào mô tả câu hỏi thay vì tiêu đề một câu trả lời có thể là

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

Như đã đề cập trong câu trả lời khác , không. Thậm chí enum classkhông phải là một lớp học.


Thông thường cần phải có các phương pháp cho một enumkết quả từ lý do đó không phải là một enum thông thường (chỉ tăng dần), mà là định nghĩa bitwise của các giá trị được che dấu hoặc cần các phép toán số học bit khác:

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;

Rõ ràng người ta nghĩ đến việc đóng gói các hoạt động cần thiết để đặt lại / đặt các bit đơn lẻ, ví dụ như giá trị mặt nạ bit hoặc các hoạt động điều khiển chỉ số bit sẽ hữu ích cho việc thao tác một tập hợp các 'cờ' như vậy.

Các struct/ class đặc điểm kỹ thuật chỉ hỗ trợ phạm vi tốt hơn của các giá trị enum để truy cập. Không nhiều không ít!

Các cách để thoát khỏi hạn chế, bạn không thể khai báo các phương thức cho enum (các lớp) , hoặc là sử dụng std::bitset(lớp bao bọc) hoặc bitfieldunion .

unions và các hiệp hội bitfield như vậy có thể có các phương thức (xem tại đây để biết các hạn chế!).

Tôi có một mẫu, cách chuyển đổi các giá trị mặt nạ bit (như được hiển thị ở trên) thành các chỉ số bit tương ứng của chúng, có thể được sử dụng dọc theo std::bitsetđây: BitIndexConverter.hpp
Tôi thấy điều này khá hữu ích để tăng cường khả năng đọc của một số 'cờ' thuật toán.


36
Có nhiều trường hợp sử dụng bảo đảm các phương thức trên enum classe, ví dụ: toString () và fromString (). Mọi ngôn ngữ chính hiện đại (thậm chí không phải vậy) đều có ngôn ngữ này (ví dụ C #, Java, Swift), không phải C ++.
Mike Lischke 20/07/2015

1
Hãy hy vọng cú pháp cuộc gọi hợp nhất vào lần tới vào khoảng ... open-std.org/jtc1/sc22/wg21/docs/ con / 2014 / n4165.pdf
sdgfsdh

4

Có một khả năng khá tương thích (§) để cấu trúc lại một enum vào một lớp mà không phải viết lại mã của bạn, điều đó có nghĩa là bạn có thể làm những gì bạn yêu cầu một cách hiệu quả mà không cần chỉnh sửa quá nhiều.

(§) như ElementW chỉ ra trong một nhận xét, mã phụ thuộc type_traits sẽ không hoạt động, vì vậy, ví dụ: người ta không thể sử dụng tự động, v.v. và luôn luôn là một sai lầm khi lật đổ C ++

các enum structenum classđặc điểm kỹ thuật về Phạm vi nên không nằm trong này.

Enum ban đầu của bạn là ví dụ 'pet' (đây chỉ là một ví dụ!).

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

(1) Bạn sửa đổi điều đó thành ví dụ petEnum (để ẩn nó khỏi mã hiện tại của bạn).

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

(2) Bạn thêm một khai báo lớp mới bên dưới nó (được đặt tên bằng enum gốc)

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) Bây giờ bạn có thể thêm bất kỳ phương thức lớp nào bạn muốn vào lớp thú cưng của bạn. ví dụ. một toán tử chuỗi

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

Bây giờ bạn có thể sử dụng ví dụ 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;
}

1
không hoàn toàn tương thích: nếu bạn sử dụng các giá trị enum với bất kỳ loại khấu trừ nào trong đó nó được dự kiến ​​sẽ có một kiểu chữ pet/ thể hiện, có thể là các mẫu auto, hoặc decltype, điều này sẽ phá vỡ, khi bạn nhận được một petEnumthay thế.
ElementW

0

Nó có thể không đáp ứng tất cả các nhu cầu của bạn, nhưng với các nhà khai thác không phải thành viên, bạn vẫn có thể có rất nhiều niềm vui. Ví dụ:

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

Điều này cho phép mã như

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"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.