Declaring an enum within a class
Solution 1
If
Color
is something that is specific to justCar
s then that is the way you would limit its scope. If you are going to have anotherColor
enum that other classes use then you might as well make it global (or at least outsideCar
).It makes no difference. If there is a global one then the local one is still used anyway as it is closer to the current scope. Note that if you define those function outside of the class definition then you'll need to explicitly specify
Car::Color
in the function's interface.
Solution 2
Nowadays - using C++11 - you can use enum class for this:
enum class Color { RED, BLUE, WHITE };
AFAII this does exactly what you want.
Solution 3
I prefer following approach (code below). It solves the "namespace pollution" problem, but also it is much more typesafe (you can't assign and even compare two different enumerations, or your enumeration with any other built-in types etc).
struct Color
{
enum Type
{
Red, Green, Black
};
Type t_;
Color(Type t) : t_(t) {}
operator Type () const {return t_;}
private:
//prevent automatic conversion for any other built-in types such as bool, int, etc
template<typename T>
operator T () const;
};
Usage:
Color c = Color::Red;
switch(c)
{
case Color::Red:
//некоторый код
break;
}
Color2 c2 = Color2::Green;
c2 = c; //error
c2 = 3; //error
if (c2 == Color::Red ) {} //error
If (c2) {} error
I create macro to facilitate usage:
#define DEFINE_SIMPLE_ENUM(EnumName, seq) \
struct EnumName {\
enum type \
{ \
BOOST_PP_SEQ_FOR_EACH_I(DEFINE_SIMPLE_ENUM_VAL, EnumName, seq)\
}; \
type v; \
EnumName(type v) : v(v) {} \
operator type() const {return v;} \
private: \
template<typename T> \
operator T () const;};\
#define DEFINE_SIMPLE_ENUM_VAL(r, data, i, record) \
BOOST_PP_TUPLE_ELEM(2, 0, record) = BOOST_PP_TUPLE_ELEM(2, 1, record),
Usage:
DEFINE_SIMPLE_ENUM(Color,
((Red, 1))
((Green, 3))
)
Some references:
- Herb Sutter, Jum Hyslop, C/C++ Users Journal, 22(5), May 2004
- Herb Sutter, David E. Miller, Bjarne Stroustrup Strongly Typed Enums (revision 3), July 2007
Solution 4
In general, I always put my enums in a struct
. I have seen several guidelines including "prefixing".
enum Color
{
Clr_Red,
Clr_Yellow,
Clr_Blue,
};
Always thought this looked more like C
guidelines than C++
ones (for one because of the abbreviation and also because of the namespaces in C++
).
So to limit the scope we now have two alternatives:
- namespaces
- structs/classes
I personally tend to use a struct
because it can be used as parameters for template programming while a namespace cannot be manipulated.
Examples of manipulation include:
template <class T>
size_t number() { /**/ }
which returns the number of elements of enum inside the struct T
:)
Solution 5
If you are creating a code library, then I would use namespace. However, you can still only have one Color enum inside that namespace. If you need an enum that might use a common name, but might have different constants for different classes, use your approach.
Related videos on Youtube
bporter
Updated on November 02, 2020Comments
-
bporter over 3 years
In the following code snippet, the
Color
enum is declared within theCar
class in order to limit the scope of the enum and to try not to "pollute" the global namespace.class Car { public: enum Color { RED, BLUE, WHITE }; void SetColor( Car::Color color ) { _color = color; } Car::Color GetColor() const { return _color; } private: Car::Color _color; };
(1) Is this a good way to limit the scope of the
Color
enum? Or, should I declare it outside of theCar
class, but possibly within its own namespace or struct? I just came across this article today, which advocates the latter and discusses some nice points about enums: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums.(2) In this example, when working within the class, is it best to code the enum as
Car::Color
, or would justColor
suffice? (I assume the former is better, just in case there is anotherColor
enum declared in the global namespace. That way, at least, we are explicit about the enum to we are referring.) -
Matthieu M. about 14 years2. Yes and no.
Car::Color getColor()
butvoid Car::setColor(Color c)
because insetColor
we already have the specifier. -
jmucchiello about 14 yearsI like this. It also forces the enum to be instantiated with a valid value. I do think an assignment operator and copy constructor would be useful. Also t_ should be private. The macros I can do without.
-
anio over 13 yearsI too like this. Thanks for the references.
-
Nawaz about 11 yearsYou said : "also it is much more typesafe (you can't assign and even compare two different enumerations ...". Why do you think it is a good feature? I think
if(c2 == Color::Red )
is reasonable and must compile, but in your example it doesn't. Same argument for assignment also! -
Victor K almost 11 years@Nawaz
c2
is of another type (Color2
), so why do you thinkc2 == Color::Red
and assignments should compile? What ifColor::Red
is 1, andColor2::Red
is 2? ShouldColor::Red == Color2::Red
evaluate totrue
orfalse
? If you mix non-typesafe enumerators, you're gonna have a bad time. -
Nawaz almost 11 years@VictorK: Ohh. I didn't notice that
c2
is of different type! Now this post makes sense to me. Thanks. (+1) -
Zingam over 10 yearsWhy isnt Type t_; private?
-
Andreas detests censorship about 5 yearsSadly, it doesn't allow member functions: stackoverflow.com/a/53284026/7395227