C ++ 열거 형 클래스에 메소드가있을 수 있습니까?


145

두 개의 값을 가진 열거 형 클래스가 있고 값을 받고 다른 값을 반환하는 메서드를 만들고 싶습니다. 또한 형식 안전성을 유지하려고합니다 (따라서 열거 형 대신 열거 형 클래스를 사용하는 이유).

http://www.cplusplus.com/doc/tutorial/other_data_types/ 는 메소드에 대해 언급하지 않았지만 모든 유형의 클래스가 메소드를 가질 수 있다는 인상을 받았습니다.


4
아닙니다. 여기를 참조 하십시오 .
juanchopanza

@octavian 내 답변에 주목 하고 사용 사례에 대해 다시 생각해보십시오!
πάντα ῥεῖ

@ πάνταῥεῖ 당신은 완전히 옳습니다, 나는 열거 형을 읽었지만 노조를 생각하고 주석을 죽였습니다.
Eugen Constantin Dinca

@octavian 있습니까 당신도 전혀 특정 사용 사례를 요구, 또는 당신은 단지에 대한 기준을 제한하고 싶지 않은 C ++ (11) enum class/struct 확인을?
πάντα ῥεῖ

나는 마음에 사용했습니다 ... 그리고 이것은 근본적인 문제였습니다
Octavian

답변:


118

아니요, 그들은 할 수 없습니다.

나는 것을 이해할 수있는 enum classC ++ 11의 강력한 형식의 열거에 대한 부분은 당신이 있음을 암시하는 보일 수도 enum있다 class너무 특성을하지만 그렇지 않다. 교육받은 추측은 키워드 선택이 범위가 지정된 열거 형을 얻기 위해 C ++ 11 이전에 사용한 패턴에서 영감을 얻은 것입니다.

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

그러나 그것은 단지 구문입니다. 다시, enum class하지 않은 것입니다 class.


88
## C ++에서 "c ++는 가능한 한 혼란스럽고 전문가 친화적 인 것을 목표로한다" 고 들었습니다 . 분명히 그것은 농담이지만, 당신은 아이디어를 얻습니다 :)
Stefano Sanfilippo

4
A union도 John Doe가 수업을 고려한 것이 아닙니다 . 그러나 멤버 함수를 가질 수 있습니다. 그리고 클래스는 멤버 함수에 필수적이지 않습니다. 유사한 지정자 사용 value또는 this같은 뭔가 enum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; }(여기 또한 수를 ;) 그냥 기능을 다른 형태의 많은 의미로 만들 수 있습니다.
Sebastian Mach

85

"당신이 할 수 없습니다"라는 대답은 기술적으로 정확하지만 다음 아이디어를 사용하여 찾고있는 행동을 달성 할 수 있다고 생각합니다.

나는 당신이 다음과 같은 것을 쓰고 싶다고 상상합니다.

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

그리고 코드가 다음과 같이 표시되기를 바랐습니다.

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

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

...

그러나 열거 형은 메소드를 가질 수 없으므로 (이것은 위의 맥락에서 아무것도 의미하지 않기 때문에) 물론 작동하지 않습니다.

그러나 비 클래스 열거 형과 해당 유형의 값을 포함하는 단일 멤버 변수를 포함하는 일반 클래스의 아이디어를 사용하면 원하는 구문 / 동작 / 유형 안전성에 매우 근접 할 수 있습니다. 즉 :

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

이제 당신은 쓸 수 있습니다 :

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

그리고 컴파일러는 다음과 같은 것을 방지합니다.

Fruit f = 1;  // Compile time error.

다음과 같은 메소드를 쉽게 추가 할 수 있습니다.

Fruit f("Apple");

f.ToString();

지원 될 수 있습니다.


1
IsYellow (), operator ==,! =도 constexpr로 표시해서는 안됩니까?
Jarek C

"오류 : 토큰"스위치 "앞에 이진 연산자가 없습니다"
Pedro77

18

제목 대신 질문에 대한 설명에 집중하면 가능한 대답은

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

다른 답변 에서 언급했듯이 아닙니다. 심지어 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(래퍼 클래스), 또는 비트 필드를union .

union비트 필드 공용체 에는 메소드 있을 있습니다 ( 제한 사항 은 여기 참조 ).

비트 마스크 값 (위 그림 참조)을 해당 비트 인덱스로 변환하는 방법에 대한 샘플이 std::bitset있습니다. BitIndexConverter.hpp
나는 이것이 '플래그'디 시즌 기반의 가독성을 향상시키는 데 매우 유용하다는 것을 알았 습니다. 알고리즘.


36
열거 형 클래스에서 메소드를 보증하는 더 많은 유스 케이스가 있습니다 (예 : toString () 및 fromString ()). 현대의 모든 주요 언어에는 C ++이 아닌 C #, Java, Swift 등이 있습니다.
Mike Lischke

1
다음에 통일 된 전화 구문을 기대합시다 ... open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4165.pdf
sdgfsdh

4

꽤 호환 기능 코드 것을 의미 다시 작성할 필요없이 클래스에 열거를 리팩토링하는 (§) 을 효과적으로 수행 할 수 있습니다 당신이 너무 많이 편집없이 할 것을 요청했다 무엇을.

(§) ElementW가 주석에서 지적한 것처럼 type_traits 종속 코드는 작동하지 않습니다. 예를 들어 자동을 사용할 수 없습니다. 그런 것들을 처리하는 방법이있을 수 있지만 결국에는 열거 형을 클래스로 변환합니다. 그리고 항상 C ++을 파괴하는 것은 실수입니다

enum structenum class사양은 그래서이의 일부를 범위 지정에 대한 있습니다.

원래 열거 형은 예를 들어 '애완 동물'입니다 (이것은 단지 예일뿐입니다!).

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

1
완전히 호환 되지않습니다 . 유형 이름 pet/ 인스턴스 를 얻을 것으로 예상되는 유형 공제와 함께 열거 형 값을 사용하는 경우 대신 템플릿 auto, 또는 decltype로 인해 중단 petEnum됩니다.
ElementW 2016 년

0

모든 요구 사항을 충족 시키지는 못하지만 비회원 연산자를 사용하면 여전히 많은 즐거움을 누릴 수 있습니다. 예를 들면 다음과 같습니다.

#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"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.