열거 형 유형에 대한 네임 스페이스-모범 사례


102

종종 여러 개의 열거 형이 함께 필요합니다. 때로는 이름 충돌이 있습니다. 이에 대한 두 가지 해결책이 떠 오릅니다. 네임 스페이스를 사용하거나 '더 큰'열거 형 요소 이름을 사용하는 것입니다. 그럼에도 불구하고 네임 스페이스 솔루션에는 두 가지 구현이 가능합니다. 하나는 중첩 된 열거 형이있는 더미 클래스 또는 완전한 네임 스페이스입니다.

세 가지 접근 방식의 장단점을 찾고 있습니다.

예:

// oft seen hand-crafted name clash solution
enum eColors { cRed, cColorBlue, cGreen, cYellow, cColorsEnd };
enum eFeelings { cAngry, cFeelingBlue, cHappy, cFeelingsEnd };
void setPenColor( const eColors c ) {
    switch (c) {
        default: assert(false);
        break; case cRed: //...
        break; case cColorBlue: //...
        //...
    }
 }


// (ab)using a class as a namespace
class Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
class Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
void setPenColor( const Colors::e c ) {
    switch (c) {
        default: assert(false);
        break; case Colors::cRed: //...
        break; case Colors::cBlue: //...
        //...
    }
 }


 // a real namespace?
 namespace Colors { enum e { cRed, cBlue, cGreen, cYellow, cEnd }; };
 namespace Feelings { enum e { cAngry, cBlue, cHappy, cEnd }; };
 void setPenColor( const Colors::e c ) {
    switch (c) {
        default: assert(false);
        break; case Colors::cRed: //...
        break; case Colors::cBlue: //...
        //...
    }
  }

19
우선 Color :: Red, Feeling : Angry 등을 사용합니다
abatishchev

좋은 질문입니다. 네임 스페이스 방법을 사용했습니다 ....;)
MiniScalope

18
모든 것의 "c"접두사는 가독성을 떨어 뜨립니다.
사용자

8
@User : 왜, 헝가리어 토론을 열어 주셔서 감사합니다 :)
xtofl

5
에서 enum e {...}와 같이 열거 형의 이름을 지정할 필요가 없습니다 . 열거 형은 익명이 될 수 있습니다. 즉 enum {...}, 네임 스페이스 나 클래스로 래핑 될 때 훨씬 더 의미가 있습니다.
kralyk

답변:


73

원래 C ++ 03 답변 :

(이상 ) 의 이점 은 원할 때 선언 을 사용할 수 있다는 것 입니다.namespaceclassusing

를 사용할 때 의 문제namespace 는 네임 스페이스가 코드의 다른 곳에서 확장 될 수 있다는 것입니다. 대규모 프로젝트에서는 두 개의 별개 열거 형이 둘 다 호출되었다고 생각하지 않는다고 보장 할 수 없습니다.eFeelings

더 간단한 코드의 struct경우 내용이 공개되기를 원하므로를 사용합니다.

이러한 관행 중 하나를 수행하는 경우 곡선보다 앞서 있으므로 더 자세히 조사 할 필요가 없습니다.

최신 C ++ 11 조언 :

C ++ 11 이상을 사용하는 경우 enum class는 열거 형 이름 내에서 열거 형 값의 범위를 암시 적으로 지정합니다.

이를 통해 enum class암시 적 변환 및 정수 유형 비교를 잃게되지만 실제로는 모호하거나 버그가있는 코드를 발견하는 데 도움이 될 수 있습니다.


4
나는 struct-idea에 동의합니다. 그리고 칭찬 주셔서 감사합니다 :)
xtofl

3
+1 C ++ 11 "enum class"구문이 기억 나지 않습니다. 해당 기능이 없으면 열거 형이 불완전합니다.
Grault

"열거 형 클래스"의 암시 적 범위에 "사용"을 사용하는 옵션입니다. 예 : 'using Color :: e;'추가 코드에 'cRed'사용을 허용하고 이것이 Color :: e :: cRed 여야한다는 것을 알고 있습니까?
JVApen 2015 년


11

나는 다음과 같은 것에 대해 앞의 답변을 혼성화했습니다. (편집 : 이것은 C ++ 11 이전에만 유용합니다. C ++ 11을 사용하는 경우 enum class)

이 열거 형은 작업자 클래스간에 공유되고 작업자 클래스 자체에 열거 형을 넣는 것은 의미가 없기 때문에 모든 프로젝트 열거 형을 포함하는 하나의 큰 헤더 파일이 있습니다.

struct문법적하고는 : 대중을 방지 typedef실제로 다른 작업자 클래스 내에서이 열거 형의 변수를 선언 할 수 있습니다.

네임 스페이스를 사용하는 것이 전혀 도움이되지 않는다고 생각합니다. 제가 C # 프로그래머이기 때문일 수 있으며 값을 참조 할 때 열거 형 이름을 사용해야하므로 익숙합니다.

    struct KeySource {
        typedef enum { 
            None, 
            Efuse, 
            Bbram
        } Type;
    };

    struct Checksum {
        typedef enum {
            None =0,
            MD5 = 1,
            SHA1 = 2,
            SHA2 = 3
        } Type;
    };

    struct Encryption {
        typedef enum {
            Undetermined,
            None,
            AES
        } Type;
    };

    struct File {
        typedef enum {
            Unknown = 0,
            MCS,
            MEM,
            BIN,
            HEX
        } Type;
    };

...

class Worker {
    File::Type fileType;
    void DoIt() {
       switch(fileType) {
       case File::MCS: ... ;
       case File::MEM: ... ;
       case File::HEX: ... ;
    }
}

9

나는 이것을 위해 클래스를 사용하지 않을 것이다. 대신 네임 스페이스를 사용하십시오. 질문은 네임 스페이스를 사용할 것인지 아니면 열거 형 값에 고유 한 ID를 사용할 것인지로 귀결됩니다. 개인적으로 저는 네임 스페이스를 사용하여 내 ID가 더 짧고 자명 할 수 있기를 바랍니다. 그런 다음 애플리케이션 코드는 'using namespace'지시문을 사용하여 모든 것을 더 읽기 쉽게 만들 수 있습니다.

위의 예에서 :

using namespace Colors;

void setPenColor( const e c ) {
    switch (c) {
        default: assert(false);
        break; case cRed: //...
        break; case cBlue: //...
        //...
    }
}

클래스보다 네임 스페이스를 선호하는 이유에 대한 힌트를 줄 수 있습니까?
xtofl

@xtofl : '클래스 색상 사용'을 쓸 수 없습니다
MSalters

2
@MSalters : Colors someColor = Red;네임 스페이스가 유형을 구성하지 않기 때문에를 쓸 수도 없습니다 . Colors::e someColor = Red;대신 작성해야하는데 , 이는 직관적이지 않습니다.
SasQ

@SasQ 문 에서 사용하고 싶다면 Colors::e someColora와 함께 struct/class사용할 필요가 switch없습니까? 익명 enum을 사용하는 경우 스위치는 struct.
Macbeth 's Enigma 2013 년

1
미안하지만 const e c거의 읽을 수없는 것 같습니다. :-) 그렇게하지 마세요. 그러나 네임 스페이스를 사용하는 것은 괜찮습니다.
dhaumann

7

클래스 또는 네임 스페이스 사용의 차이점은 네임 스페이스처럼 클래스를 다시 열 수 없다는 것입니다. 이렇게하면 나중에 네임 스페이스가 남용 될 가능성을 피할 수 있지만 열거 형 집합에도 추가 할 수없는 문제도 있습니다.

클래스 사용의 가능한 이점은 네임 스페이스의 경우가 아닌 템플릿 유형 인수로 사용할 수 있다는 것입니다.

class Colors {
public:
  enum TYPE {
    Red,
    Green,
    Blue
  };
};

template <typename T> void foo (T t) {
  typedef typename T::TYPE EnumType;
  // ...
}

개인적으로 저는을 사용 하는 것을 좋아하지 않으며 정규화 된 이름을 선호하므로 네임 스페이스에 대한 플러스로 생각하지 않습니다. 그러나 이것은 아마도 프로젝트에서 내리게 될 가장 중요한 결정은 아닐 것입니다!


수업을 다시 열지 않는 것도 잠재적 인 단점입니다. 색상 목록도 유한하지 않습니다.
MSalters

1
클래스를 repoening하지 않는 것이 잠재적 인 이점이라고 생각합니다. 더 많은 색상을 원하면 더 많은 색상으로 클래스를 다시 컴파일합니다. 내가 이것을 할 수 없다면 (코드가 없다고 말하면) 어떤 경우에도 그것을 만지고 싶지 않습니다.
Thomas Eding 2011-08-24

@MSalters : 수업을 다시 열 수 없다는 것은 단점 일뿐만 아니라 안전 도구이기도합니다. 클래스를 다시 열고 일부 값을 열거 형에 추가 할 수있을 때 이미 해당 열거 형에 의존하고 이전 값 집합 만 알고있는 다른 라이브러리 코드가 손상 될 수 있기 때문입니다. 그런 다음 새 값을 기꺼이 받아들이지 만 런타임에 값으로 무엇을해야할지 알지 못합니다. Open-Closed 원칙을 기억하십시오 : 클래스는 수정을 위해 닫혀 야 하지만 확장을 위해 열려 있어야합니다 . 확장한다는 것은 기존 코드에 추가하는 것이 아니라 새 코드로 감싸는 것을 의미합니다 (예 : 파생).
SasQ

따라서 열거 형을 확장하려면 첫 번째 유형에서 파생 된 새 유형으로 만들어야합니다 (C ++에서 쉽게 가능하다면 ...; /). 그런 다음 새 값을 이해하는 새 코드에서 안전하게 사용할 수 있지만 이전 코드에서는 이전 값만 허용됩니다 (아래로 변환). 새 값 중 어떤 것도 잘못된 유형 (확장 된 유형)으로 받아들이지 않아야합니다. 그들이 이해하는 이전 값만 올바른 (기본) 유형으로 허용됩니다 (그리고 실수로 새 유형이므로 새 코드에서도 허용 될 수 있음).
SasQ

7

클래스 사용의 장점은 그 위에 본격적인 클래스를 구축 할 수 있다는 것입니다.

#include <cassert>

class Color
{
public:
    typedef enum
    {
        Red,
        Blue,
        Green,
        Yellow
    } enum_type;

private:
    enum_type _val;

public:
    Color(enum_type val = Blue)
        : _val(val)
    {
        assert(val <= Yellow);
    }

    operator enum_type() const
    {
        return _val;
    }
};

void SetPenColor(const Color c)
{
    switch (c)
    {
        case Color::Red:
            // ...
            break;
    }
}

위의 예에서 볼 수 있듯이 클래스를 사용하여 다음을 수행 할 수 있습니다.

  1. (슬프게도 컴파일 타임이 아닌) C ++에서 잘못된 값에서 캐스트를 허용하지 않습니다.
  2. 새로 생성 된 열거 형에 대해 (0이 아닌) 기본값을 설정합니다.
  3. 선택의 문자열 표현을 반환하는 것과 같은 추가 메서드를 추가하십시오.

operator enum_type()C ++에서 클래스를 기본 열거 형으로 변환하는 방법을 알 수 있도록 선언해야합니다 . 그렇지 않으면 switch문에 유형을 전달할 수 없습니다 .


이 솔루션이 여기에 표시된 것과 어떻게 든 관련이 있습니까? : en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Safe_Enum 매번이 패턴을 다시 작성할 필요가없는 템플릿으로 만드는 방법에 대해 생각하고 있습니다. 나는 그것을 사용해야합니다.
SasQ

@SasQ : 비슷해 보입니다. 그것은 아마도 같은 생각 일 것입니다. 그러나 거기에 '일반적인'방법을 많이 추가하지 않는 한 템플릿이 유익한 지 확실하지 않습니다.
Michał Górny

1. 사실이 아닙니다. enum이 유효한지, const_expr 또는 int에 대한 개인 생성자를 통해 컴파일 시간에 확인할 수 있습니다.
xryl669 2014 년

5

열거 형은 둘러싸는 범위로 범위가 지정 되므로 전역 네임 스페이스 오염을 방지하고 이름 충돌을 방지하기 위해 열거 형을 어떤 것으로 래핑하는 것이 가장 좋습니다 . 나는 클래스보다 네임 스페이스를 선호하는 이유 namespace는 단순히 들고있는 가방처럼 class느껴지 지만 강력한 객체처럼 느껴 지기 때문입니다 ( struct비교 class토론). 네임 스페이스의 가능한 이점은 나중에 확장 할 수 있다는 것입니다. 수정할 수없는 타사 코드를 처리하는 경우 유용합니다.

물론 C ++ 0x로 enum 클래스를 얻을 때 이것은 모두 문제입니다.


enum 클래스 ... 찾아봐야합니다!
xtofl

3

또한 열거 형을 클래스로 래핑하는 경향이 있습니다.

Richard Corden에서 알 수 있듯이 클래스의 이점은 C ++ 의미의 유형이므로 템플릿과 함께 사용할 수 있다는 것입니다.

기본 기능을 제공하는 모든 템플릿에 특화된 특별한 toolbox :: Enum 클래스가 있습니다 (주로 : I / O가 읽기 쉽도록 enum 값을 std :: string에 매핑).

내 작은 템플릿에는 허용 된 값을 실제로 확인하는 추가 이점도 있습니다. 컴파일러는 값이 실제로 열거 형에 있는지 확인하는 데 약간 느슨합니다.

typedef enum { False: 0, True: 2 } boolean;
   // The classic enum you don't want to see around your code ;)

int main(int argc, char* argv[])
{
  boolean x = static_cast<boolean>(1);
  return (x == False || x == True) ? 0 : 1;
} // main

당신이 의미가없는 (그리고 당신이 기대하지 않을) 열거 형 값을 남겼 기 때문에 컴파일러가 이것을 잡지 않을 것이라는 것이 항상 나를 괴롭 혔습니다.

비슷하게:

typedef enum { Zero: 0, One: 1, Two: 2 } example;

int main(int argc, char* argv[])
{
  example y = static_cast<example>(3);
  return (y == Zero || y == One || y == Two) ? 0 : 1;
} // main

다시 한 번 main은 오류를 반환합니다.

문제는 컴파일러가 사용 가능한 가장 작은 표현 (여기서는 2 비트 필요)에 열거 형을 맞추고이 표현에 맞는 모든 것이 유효한 값으로 간주된다는 것입니다.

또한 열거 형에 값을 추가 할 때마다 스위치를 모두 수정할 필요가 없도록 스위치 대신 가능한 값에 대한 루프를 사용하려는 경우가 있습니다.

내 작은 도우미는 모두 내 열거 형을 쉽게 처리 할 수 ​​있으며 (물론 약간의 오버 헤드를 추가 함) 각 열거 형을 자체 구조체에 중첩하기 때문에 가능합니다. :)


4
흥미 롭군. Enum 클래스의 정의를 공유 하시겠습니까?
momeara
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.