열거 형 유형에 대한 네임 스페이스-모범 사례
종종 여러 개의 열거 형이 함께 필요합니다. 때로는 이름 충돌이 있습니다. 이에 대한 두 가지 해결책이 떠 오릅니다. 네임 스페이스를 사용하거나 '더 큰'열거 형 요소 이름을 사용하는 것입니다. 그럼에도 불구하고 네임 스페이스 솔루션에는 두 가지 구현이 가능합니다. 하나는 중첩 된 열거 형이있는 더미 클래스 또는 완전한 네임 스페이스입니다.
세 가지 접근 방식의 장단점을 찾고 있습니다.
예:
// 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: //...
//...
}
}
원래 C ++ 03 답변 :
(이상 ) 의 이점 은 원할 때 선언 을 사용할 수 있다는 것 입니다.namespace
class
using
를 사용할 때 의 문제namespace
는 네임 스페이스가 코드의 다른 곳에서 확장 될 수 있다는 것입니다. 대규모 프로젝트에서는 두 개의 별개 열거 형이 둘 다 호출되었다고 생각하지 않는다고 보장 할 수 없습니다.eFeelings
더 간단한 코드의 struct
경우 내용이 공개되기를 원하므로를 사용합니다.
이러한 관행 중 하나를 수행하는 경우 곡선보다 앞서 있으므로 더 자세히 조사 할 필요가 없습니다.
최신 C ++ 11 조언 :
C ++ 11 이상을 사용하는 경우 enum class
는 열거 형 이름 내에서 열거 형 값의 범위를 암시 적으로 지정합니다.
이를 통해 enum class
암시 적 변환 및 정수 유형 비교를 잃게되지만 실제로는 모호하거나 버그가있는 코드를 발견하는 데 도움이 될 수 있습니다.
참고로 C ++ 0x에는 언급 한 것과 같은 경우에 대한 새로운 구문이 있습니다 ( C ++ 0x 위키 페이지 참조 ).
enum class eColors { ... };
enum class eFeelings { ... };
나는 다음과 같은 것에 대해 앞의 답변을 혼성화했습니다. (편집 : 이것은 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: ... ;
}
}
I would definitely avoid using a class for this; use a namespace instead. The question boils down to whether to use a namespace or to use unique ids for the enum values. Personally, I'd use a namespace so that my ids could be shorter and hopefully more self-explanatory. Then application code could use a 'using namespace' directive and make everything more readable.
From your example above:
using namespace Colors;
void setPenColor( const e c ) {
switch (c) {
default: assert(false);
break; case cRed: //...
break; case cBlue: //...
//...
}
}
An difference between using a class or a namespace is that the class cannot be reopened like a namespace can. This avoids the possibility that the namespace might be abused in the future, but there is also the problem that you cannot add to the set of enumerations either.
A possible benefit for using a class, is that they can be used as template type arguments, which is not the case for namespaces:
class Colors {
public:
enum TYPE {
Red,
Green,
Blue
};
};
template <typename T> void foo (T t) {
typedef typename T::TYPE EnumType;
// ...
}
Personally, I'm not a fan of using, and I prefer the fully qualified names, so I don't really see that as a plus for namespaces. However, this is probably not the most important decision that you'll make in your project!
Advantage of using a class is that you can build a full-fledged class on top of it.
#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;
}
}
As the above example shows, by using a class you can:
- prohibit (sadly, not compile-time) C++ from allowing a cast from invalid value,
- set a (non-zero) default for newly-created enums,
- add further methods, like for returning a string representation of a choice.
Just note that you need to declare operator enum_type()
so that C++ would know how to convert your class into underlying enum. Otherwise, you won't be able to pass the type to a switch
statement.
Since enums are scoped to their enclosing scope, it's probably best to wrap them in something to avoid polluting the global namespace and to help avoid name collisions. I prefer a namespace to class simply because namespace
feels like a bag of holding, whereas class
feels like a robust object (cf. the struct
vs. class
debate). A possible benefit to a namespace is that it can be extended later - useful if you're dealing with third-party code that you cannot modify.
This is all moot of course when we get enum classes with C++0x.
I also tend to wrap my enums in classes.
As signaled by Richard Corden, the benefit of a class is that it is a type in the c++ sense and so you can use it with templates.
I have special toolbox::Enum class for my needs that I specialize for every templates which provides basic functions (mainly: mapping an enum value to a std::string so that I/O are easier to read).
My little template also has the added benefit of really checking for the allowed values. The compiler is kind of lax on checking if the value really is in the enum:
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
It always bothered me that the compiler will not catch this, since you are left with an enum value that has no sense (and that you won't expect).
Similarly:
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
Once again main will return an error.
The problem is that the compiler will fit the enum in the smallest representation available (here we need 2 bits) and that everything that fits in this representation is considered a valid value.
There is also the problem that sometimes you'd rather have a loop on the possible values instead of a switch so that you don't have to modify all you switches each time you add a value to the enum.
All in all my little helper really ease things for my enums (of course, it adds some overhead) and it is only possible because I nest each enum in its own struct :)
참고URL : https://stackoverflow.com/questions/482745/namespaces-for-enum-types-best-practices
'programing tip' 카테고리의 다른 글
Haskell printf는 어떻게 작동합니까? (0) | 2020.08.24 |
---|---|
Javascript 웹 앱 및 Java 서버, Maven에서 모두 빌드하거나 웹 앱용 Grunt를 사용 하시겠습니까? (0) | 2020.08.24 |
setAccessible을 "합법적 인"용도로만 제한하는 방법은 무엇입니까? (0) | 2020.08.24 |
Backbone.View "el"혼란 (0) | 2020.08.24 |
tuple (set ([1, "a", "b", "c", "z", "f"])) == tuple (set ([ "a", "b", "c", "z", "f", 1])) 해시 무작위 화가 활성화 된 시간의 85 %? (0) | 2020.08.24 |