Allow for Range-Based For with enum classes?
Iterating enumerations with the enumeration itself as an iterator is a poor idea, and I recommend using an actual iterator as in deft_code's answer. But if this is really what you want:
COLOR operator++(COLOR& x) {
return x = (COLOR)(std::underlying_type<COLOR>::type(x) + 1);
}
COLOR operator*(COLOR c) {
return c;
}
COLOR begin(COLOR r) {
return COLOR::First;
}
COLOR end(COLOR r) {
COLOR l=COLOR::Last;
return ++l;
}
int main() {
//note the parenthesis after COLOR to make an instance
for(const auto& c : COLOR()) {
//do work
}
return 0;
}
Working here: http://ideone.com/cyTGD8
On the iterator side of things, the easiest way is simply:
const COLOR COLORS[] = {COLOR::Blue, COLOR::Red, COLOR::Green, COLOR::Purple};
const COLOR (&COLORREF)[(int)COLOR::Last+1] = COLORS;
int main() {
for(const auto& c : COLORS) {
//do work
}
return 0;
}
As seen here: http://coliru.stacked-crooked.com/a/5d356cc91556d6ef
(The separate defintinion and the reference of the array makes it a compiler error if the number of colors doesn't match the number of elements in the array. Excellent easy safety check.)
How to use enum class values as part of for-loop?
I would recommend doing something different.
Create a vector of Suit
and one to Rank
, and loop over them using the power of STL
const std::vector<Suit> v_suit {Suit::clubs, Suit::diamonds, Suit::hearts, Suit::spades};
const std::vector<Rank> v_rank {Rank::one, Rank::two, Rank::three, Rank::four, Rank::five,
Rank::six, Rank::seven, Rank::eight, Rank::nine, Rank::ten, Rank::jack,
Rank::queen, Rank::king, Rank::ace};
Yes, you have to type them twice, but this permits you to use whatever values you want for them (ie. not consecutive), not use awkward stuff like enum_count
(What card do you want? Give me a diamonds enum_count!!), no need for casting, and use the iterators provided to std::vector
.
To use them:
for(const auto & s : v_suit)
for (const auto & r : v_rank)
cards.push_back({s,r});
How can I iterate over an enum?
The typical way is as follows:
enum Foo {
One,
Two,
Three,
Last
};
for ( int fooInt = One; fooInt != Last; fooInt++ )
{
Foo foo = static_cast<Foo>(fooInt);
// ...
}
Please note, the enum Last
is meant to be skipped by the iteration. Utilizing this "fake" Last
enum, you don't have to update your terminating condition in the for loop to the last "real" enum each time you want to add a new enum.
If you want to add more enums later, just add them before Last. The loop in this example will still work.
Of course, this breaks down if the enum values are specified:
enum Foo {
One = 1,
Two = 9,
Three = 4,
Last
};
This illustrates that an enum is not really meant to iterate through. The typical way to deal with an enum is to use it in a switch statement.
switch ( foo )
{
case One:
// ..
break;
case Two: // intentional fall-through
case Three:
// ..
break;
case Four:
// ..
break;
default:
assert( ! "Invalid Foo enum value" );
break;
}
If you really want to enumerate, stuff the enum values in a vector and iterate over that. This will properly deal with the specified enum values as well.
What are commonly-used ways to iterate over an enum class in C++?
This is the most-readable and simplest approach I could come up with, but I am open to other peoples' example solutions.
I find this approach to be easy-to-use, similar to my C approach (making it more-portable and more-recognizable), and suitable to C++. It compiles with the -Wall -Wextra -Werror
compiler build options.
enum class MyErrorType
{
SOMETHING_1 = 0,
SOMETHING_2,
SOMETHING_3,
SOMETHING_4,
SOMETHING_5,
/// Not a valid value; this is the number of members in this enum
_COUNT,
// helpers for iterating over the enum
begin = 0,
end = _COUNT,
};
for (MyErrorType myErrorType = (MyErrorType)0;
myErrorType < MyErrorType::_COUNT;
myErrorType = static_cast<MyErrorType>((size_t)myErrorType + 1))
{
switch (myErrorType)
{
case MyErrorType::SOMETHING_1:
break;
case MyErrorType::SOMETHING_2:
break;
case MyErrorType::SOMETHING_3:
break;
case MyErrorType::SOMETHING_4:
break;
case MyErrorType::SOMETHING_5:
break;
case MyErrorType::_COUNT:
// This case will never be reached. It is included only so that when
// compiling with `-Wall -Wextra -Werror` build flags you get the
// added bonus of covering all switch cases (withOUT unnecessarily
// relying on a `default` case which would break this feature!), so
// if you ever add a new element to the enum class but forget to
// add it here to the switch case the compiler will THROW AN ERROR.
// This is an added safety benefit to force you to keep your enum
// and the switch statement in-sync! It's a technique commonly used
// in C as well.
break;
}
}
Read my comments for the MyErrorType::_COUNT
case above! If you are using the compiler's -Wall -Wextra -Werror
compiler options but do NOT include this case in the switch statement (since those build options require you to cover ALL enum cases in ALL switch statements!), the compiler will throw the following error and stop! This is a great safety feature to ensure you keep the enum definition and all switch cases in-sync, handling all possible enums in all of your switch statements. Here is the compiler error thrown by LLVM's clang compiler (an alternative to gcc):
../my_file.cpp:11:16: error: enumeration value ‘_COUNT’ not handled in switch [-Werror=switch]
11 | switch (myErrorType) {
| ^
One more tiny improvement over the code above, for clarity, would be to add begin
and end
elements to your enum like this:
enum class MyErrorType
{
SOMETHING_1 = 0,
SOMETHING_2,
SOMETHING_3,
SOMETHING_4,
SOMETHING_5,
/// Not a valid value; this is the number of members in this enum
_COUNT,
// helpers for iterating over the enum
begin = 0,
end = _COUNT,
};
...so that you can iterate over the enum as follows. The incrementing part of the for
loop still is a bit cumbersome with all of the required casts, but the initial state and end condition check are at least much clearer now, since they use MyErrorType::begin
and MyErrorType::end
:
for (MyErrorType myErrorType = MyErrorType::begin;
myErrorType < MyErrorType::end;
myErrorType = static_cast<MyErrorType>((size_t)myErrorType + 1))
{
switch (myErrorType)
{
case MyErrorType::SOMETHING_1:
break;
case MyErrorType::SOMETHING_2:
break;
case MyErrorType::SOMETHING_3:
break;
case MyErrorType::SOMETHING_4:
break;
case MyErrorType::SOMETHING_5:
break;
case MyErrorType::_COUNT:
// This case will never be reached.
break;
}
}
Related:
- Common techniques for iterating over
enum
s (as opposed toenum class
es): How can I iterate over an enum?- [my answer] How can I iterate over an enum?
- My answer on some of the differences between
enum class
es (strongly-typed enums) and regularenum
s (weakly-typed enums) in C++: How to automatically convert strongly typed enum into int? - Some of my personal notes on the
-Wall -Wextra -Werror
and other build options, from my eRCaGuy_hello_world repo. - Incrementation and decrementation of “enum class”
Other keywords: common way to iterate over enum or enum class in C or C++; best way to iterate over enum class in C++; enum class C++ iterate; c++ iterate over enum class
Misunderstanding of range-based for loop?
The range-based for loop needs a collection, like an array or a vector. The enum class isn't a collection.
However, it's C++, so there's a workaround. See: Allow for Range-Based For with enum classes?
Unable to iterate over enum
The C++11 language revision has adopted several keywords that were in use in C++/CLI. Like nullptr
, override
, final
. And the enum class
keyword. That makes your Animals type an unmanaged type in recent VS versions and Enum::GetValues() incapable of discovering enum values since it relies on reflection.
override
and final
don't byte since they are contextual keywords. nullptr
is troublesome, but it stays the managed flavor and __nullptr
is the unmanaged flavor. The workaround for enum class
is unintuitive, you must declare it with a top-level type visibility specifier (public or private). Syntax that isn't valid in native C++. Fix:
public enum class Animals { Ducks, Giraffes, Hamster };
Iteration over enum class in a project
Adding the inline
keyword in front of your 4 functions will allow them to be defined in the header without the multiple definition errors. For example:
inline COLOR operator*(COLOR c) {return c;}
Or you can include just the prototype in the .h file and define the functions in 1 .cpp file.
Related Topics
Profiling the C++ Compilation Process
Is #Pragma Once Part of the C++11 Standard
Openmp Set_Num_Threads() Is Not Working
How to Return Numpy.Array from Boost::Python
Preincrement Faster Than Postincrement in C++ - True? If Yes, Why Is It
Determine Process Info Programmatically in Darwin/Osx
Is the Data in Nested Std::Arrays Guaranteed to Be Contiguous
Using C++ to Edit the Registry
Using 'Std::Function<Void(...)>' to Call Non-Void Function
Why Do Some People Use Swap for Move Assignments
Why Is Std::Min Failing When Windows.H Is Included
How to Make Visual Studio Use the Native Amd64 Toolchain
Rotate Cv::Mat Using Cv::Warpaffine Offsets Destination Image
Inferring the Call Signature of a Lambda or Arbitrary Callable for "Make_Function"
How to Use Something Like Std::Vector<Std::Mutex>