-->

Is there any well-known paradigm for iterating enu

2020-08-12 18:12发布

问题:

I have some C++ code, in which the following enum is declared:

enum Some 
{
   Some_Alpha = 0,
   Some_Beta,
   Some_Gamma,
   Some_Total
};
int array[Some_Total];

The values of Alpha, Beta and Gamma are sequential, and I gladly use the following cycle to iterate through them:

for ( int someNo = (int)Some_Alpha; someNo < (int)Some_Total; ++someNo ) {}

This cycle is ok, until I decide to change the order of the declarations in the enum, say, making Beta the first value and Alpha - the second one. That invalidates the cycle header, because now I have to iterate from Beta to Total. So, what are the best practices of iterating through enum? I want to iterate through all the values without changing the cycle headers every time. I can think of one solution:

enum Some 
{
   Some_Start = -1,
   Some_Alpha,
   ...
   Some_Total
};
int array[Some_Total];

and iterate from (Start + 1) to Total, but it seems ugly and I have never seen someone doing it in the code. Is there any well-known paradigm for iterating through the enum, or I just have to fix the order of the enum values? (let's pretend, I really have some awesome reasons for changing the order of the enum values)...

回答1:

enum Some
{
   Some_first_ = 0,
   Some_Alpha = Some_first_,
....
   Some_last_
};

Doing such you can grant first & last never changes order



回答2:

You can define an operator++() for your enum. This has the advantage that it uses the well-known paradigm of the standard incrementation operators. :)

Depending on whether your enums are contiguous, you can treat them as int or use a switch:

Some& operator++(Some& obj)
{
# if YOUR_ENUMS_ARE_CONTIGUOUS
  int i = obj;
  if( ++i > Some_Total ) i = Some_Alpha;
  return obj = static_cast<Some>(i);
# else 
  switch(obj)
  {
    case Some_Alpha : obj = Some_Beta;  break;
    case Some_Beta  : obj = Some_Gamma; break;
    case Some_Gamma : obj = Some_Total; break;
    case Some_Total : obj = Some_Alpha; break;
    default: assert(false); // changed enum but forgot to change operator
  }
  return obj;
# endif
}

Note that, if operator++() is defined, users will probably expect an operator--(), too.



回答3:

No, there is no way of doing this because there is no guarantee that someone hasn't written code like:

enum Some 
{
   Some_Alpha = 0,
   Some_Beta,
   Some_Gamma = 42,
   Some_Delta, 
  Some_Total
};


回答4:

You can check out this article with its source code on how you can implement this with static class members.



回答5:

In C++11 (and probably earlier), you could use the following hack, to make Some iterable:

Some operator++(Some& s) {
    return s = (Some )(std::underlying_type<Some>::type(x) + 1); 
}
Some operator*(Some s) {
    return s;
} 
Some begin(Some s) {
    return Some_Alpha;

Some end(Some s) {
    return Some_Gamma;
}

int main() { 
    // the parenthesis here instantiate the enum
    for(const auto& s : Some()) { 
        // etc. etc.
    }
    return 0;
}

(This answer was shamelessly adapted from here.)



回答6:

If you do not use any assignments, the enums are guaranteed to be sequential starting with 0 as the first. thers.

The best thing you can do is keep them in the order you want in your enum definition, and cycle through them with the for loop.



回答7:

I place all Enums in their own namespace. Example:

namespace Foo {
enum Enum {
    First=0, // must be sequential
    Second,
    Third,
    End // must be last
};
}

In code:

for (int i=Foo::First; i!=Foo::End; i++) {
// do stuff
}

This is because C++ allows stuff like this (not tested in a compiler):

enum Foo {
Alpha = 1
};

enum Bar {
Beta = 2
};

Foo foo = Beta;

Which is clearly wrong.