36

I have a enumerated type StackID, and I am using the enumeration to refer to an index of a particular vector and it makes my code easier to read.

However, I now have the need to create a variable called nextAvail of type StackID. (it actually refers to a particular stackID ). I tried to increment it but in C++, the following is illegal:

nextAvail++;

Which sort of makes sense to me ... because there's no bounds checking.

I'm probably overlooking something obvious, but what's a good substitute?


I also want to link to this question.

Community
  • 1
  • 1
BeeBand
  • 10,953
  • 19
  • 61
  • 83

10 Answers10

41

I'm probably overlooking something obvious, but what's a good substitute?

Overloading operator++:

// Beware, brain-compiled code ahead! 
StackID& operator++(StackID& stackID)
{
#if MY_ENUMS_ARE_CONTIGUOUS && I_DO_NOT_WORRY_ABOUT_OVERFLOW
  return stackID = static_cast<StackID>( ++static_cast<int>(stackID) );
#else
  switch(stackID) {
    case value1 : return stackID = value2;
    case value2 : return stackID = value3;
    ...
    case valueN : return stackID = value1;
  }
  assert(false);
  return stackID; // some compilers might warn otherwise
#endif
}

StackID operator++(StackID& stackID, int)
{
  StackID tmp(stackID);
  ++stackID;
  return tmp;
}
Natan Streppel
  • 5,759
  • 6
  • 35
  • 43
sbi
  • 219,715
  • 46
  • 258
  • 445
  • @sbi - It would certainly make things simple for me to do this. but what do you say to the posters who suggest that an `enum` is meant to express a set of arbitrary values? – BeeBand Aug 13 '10 at 08:57
  • @BeeBand - if there is any use at all for the '++', I assume you have a good reason to go beyond the conventional C++ use of enums as compile-time checked ints. On the other hand, Sbi's code could do with some bound-checking. In Java, enums are just a sort of lightweight classes with unmodifiable members -- it is accepted practice to place methods in them. See http://download.oracle.com/javase/1.5.0/docs/guide/language/enums.html. – tucuxi Aug 13 '10 at 09:05
  • 4
    @BeeBand: I don't agree. Probably five out of ten textbook `enum` examples are something like `enum month { Jan, Feb, ..., Dec };` and thus a perfect example for enenumerations that can increment. During the last >15 years of doing C++, I have overloaded `operator++` for enumerations probably less than five times. But I _have_ done it. – sbi Aug 13 '10 at 09:06
  • 3
    @GMan & @Martin: `` Can you tell I'm on vacation, sitting in the garden, with my laptop on my knees twice a day and am to lazy to fire up the compiler? @BeeBand: Sorry for screwing this so badly. Thankfully, the community catches these. – sbi Aug 13 '10 at 16:26
  • @sbi Added a missing curly brace on the `switch` statement - hope that's ok. Just commenting this because I got surprised everyone passed right through it. – Natan Streppel Jan 29 '14 at 22:56
  • 1
    @Streppel: Thanks! (Of course, since I qualified my code with a warning, I don't feel bad about missing a brace... `:´^>` ) – sbi Jan 30 '14 at 09:47
  • Hi, This is working for Pre operation like ++ Var. But for Post operation like Var++, its not working.. – Bharathi Oct 07 '14 at 13:03
  • @Jasmine: That's what the second overload is for. Also have a look at the [operator overloading FAQ](http://stackoverflow.com/a/4421719/140719). – sbi Oct 08 '14 at 02:54
23

Because enumerations do not have to be contiguous. E.g. take this example:

enum Colors {
 cRed, // = 0
 cBlue, // = 1
 cGreen = 3
}

What should happen in this scenario?

Colors color = cBlue;
Colors other = color++;

Should other be cGreen or should it be 2. In that case it's not a valid enumeration member anymore. What about this?

Colors color = cGreen;
Colors other = color++;

Should other be cRed (wrap around) or 4?

As you can see, being able to increment enumeration values introduces a whole lot of questions and complicates the simple mechanism that they intend to be.

If all you care about is the integer value being incremented, then simply cast to int and increment that.

Igor Zevaka
  • 74,528
  • 26
  • 112
  • 128
  • 5
    @Igor: Now replace your example with this: `enum month ...`. Months are contiguous and naturally increment. What then? – sbi Aug 13 '10 at 09:11
  • 10
    @sbi: Oh months, that changes everything because months are simple . . . hold on, what happens when you increment December? Should it throw an overflow exception, or should it wrap around? It may be obvious to you what happens, but I may require different functionality. There are too many questions around how this *should* work, the language designers were correct to dis-allow this, and let each case be handled individually. – Binary Worrier Aug 13 '10 at 09:25
  • @Igor: Thank you! I think your answer is extremely useful, however I have accepted sbi's. I think I've kind of cheated a little here as I have basically asked 2 questions in my post. One being in the title, and I think you nailed that in your answer and the other being the last line of my post, for which I think the points go to sbi. – BeeBand Aug 13 '10 at 09:37
  • @BeeBand Hehe, that's OK. I am glad I provided answers to some of your questions. – Igor Zevaka Aug 13 '10 at 09:48
  • 3
    @Binary: I'm not sure about your calender, but around here, after December comes January. That's pretty much the same as when you increment `std::numeric_limits::max()`. Anyway, __the question was not about why it is that way__ ("Which sort of makes sense to me..."), __but what to do instead__ ("what's a good substitute?"). – sbi Aug 13 '10 at 09:52
  • @Sbi: To be honest, I took the question from the title, and didn't read much further. And yes Janurary comes after December, but it's Janurary of *next year*, which may be an imprtant distinction. I'm still glad the language doesn't directly support this, for a variety of reasons :) – Binary Worrier Aug 13 '10 at 10:42
  • @Binary: Ouch. Obviously, _I_ failed to read the question's title before I wrote this. Sorry for saying this so self-opinionated. I feel bad about that now. `:(` – sbi Aug 13 '10 at 16:38
  • @sbi: nay worries pal. If we weren't opinionated we wouldn't contribute so much on SO :) Enjoy the vacation dude ... that said SWITCH OFF YOUR COMPUTER AND GO ENJOY YOURSELF! – Binary Worrier Aug 13 '10 at 21:31
  • @Binary: @Johannes has a point there, although I'm pretty convinced he's so sure about this because he knows the feeling very well. `:)` Anyway, I had fun, went hiking a lot, but in the morning and evening I tormented my UMTS connection a bit with SO and a few other things. I did not check work stuff, though. – sbi Aug 14 '10 at 10:12
  • @sbi: Hiking! Ah man I haven't done any real hiking since I visited Alaska with my brothers and that was 4 1/2 years ago. I'd love to hit the trails again, maybe next summer! Again, enjoy :) – Binary Worrier Aug 14 '10 at 20:40
  • There is a reason C++ has the concept of UB. The answer to your question is simple: If there is no enum with a value 1 higher than the current one, an increment causes UB. – 12431234123412341234123 Oct 29 '21 at 19:24
17

Casting back and forth to/from int is of course the obvious solution, then you make clear that you understand that the addition is happening "outside" the enum:

nextAvail = static_cast<StackID>(static_cast<int>(nextAvail) + 1);
unwind
  • 391,730
  • 64
  • 469
  • 606
  • 2
    Hmm. Yes, that was my other option, I just think all those cast's make the code look kind of messy. – BeeBand Aug 13 '10 at 08:39
3

Why not store nextAvail as an int instead if you're going to do arithmetic operations on it?

Another option would be to wrap the enum in your own type and overload operator ++ for it (which also could wrap around or something for instance).

Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
  • I think that's what I'm after - wrapping the enum in my own type and then I can do what I like to it. Can you point me in the direction of an example? – BeeBand Aug 13 '10 at 08:52
2

An enumeration is semantically supposed to represent a set of distinct related, values.

So you could have

enum Colour {RED, GREEN, BLUE};

But that should be equivalent to:

enum Colour {GREEN, BLUE, RED};

The problem is that if you increment an enum then those representations are not the same. GREEN++ in the first case is not the same as GREEN++ in the second.

Making your program dependent on the declaration of the enum is a recipe for disaster - maintainers may assume that the order of the enum doesnt matter, introducing many silent bugs.

pauljwilliams
  • 19,079
  • 3
  • 51
  • 79
  • 3
    but it is possible to explictly assign the values to enums for example `enum Colour {RED = 0, GREEN = 1, BLUE = 2}`. In that case order doesn't matter. – Naveen Aug 13 '10 at 08:35
  • True, but then you're going beyond what an enum should be, and crossing into it being a set of key/value pairs. – pauljwilliams Aug 13 '10 at 08:37
  • it's not clear what you're suggesting when you say "that should be equivalent", can you elaborate a little? – BeeBand Aug 13 '10 at 08:37
  • @BeeBand: Changing the ordering within an enum should not change the behavior of code; but in your case, it will. – egrunin Aug 13 '10 at 08:40
  • Sure. The first declaration expresses the fact that there are three colours, red, green and blue. Thats all it says. But the second one says the same. A maintainer of the code may, perfectly reasonably, add a colour, YELLOW, between GREEN and BLUE. But any code that assumed (implicitly or explicitly) that GREEN + 1 = BLUE would then break, silently. In short, enums are for expressing sets of values that have a common attribute (in this case that they're colours), rather than a relationship between them (that green comes before blue, or after red etc...) – pauljwilliams Aug 13 '10 at 08:42
1

Enums are going to be type int, so you can cast them. Is this what you're trying to do?

int ndx = (int) StackID.SomeValue;
...
++ndx;

This is going to make someone very confused down the line, of course.

It occurs to me that you're using an enum where you should be using const, or even #define. enum is most appropriate when you have arbitrary values (where the exact value is not meaningful).

egrunin
  • 24,650
  • 8
  • 50
  • 93
  • I think you might be right re. const. My code assums that the exact value is meaningful, and as you ( and other posters ) have suggested, breaks the intended meaning of an enum. – BeeBand Aug 13 '10 at 08:54
  • enums are not always of type `int`. – jchl Nov 20 '19 at 08:54
1

I've overloaded the ++/-- operator in this way:

enum STATE {STATE_1, STATE_2, STATE_3, STATE_4, STATE_5, STATE_6};

// Overload the STATE++ operator

inline STATE& operator++(STATE& state, int) {
    const int i = static_cast<int>(state)+1;
    state = static_cast<STATE>((i) % 6);
    return state;
}

// Overload the STATE-- operator

inline STATE& operator--(STATE& type, int) {
    const int i = static_cast<int>(type)-1;

    if (i < 0) {
        type = static_cast<STATE>(6);
    } else {
        type = static_cast<STATE>((i) % 6);
    }
    return type;
}
Manuel Schmitzberger
  • 5,162
  • 3
  • 36
  • 45
1

I'm quite happy with this C plus C++ solution for a for loop incrementing an enum.

for (Dwg_Version_Type v = R_INVALID; v <= R_AFTER; v++)

=>

int vi;
for (Dwg_Version_Type v = R_INVALID; 
     v <= R_AFTER; 
     vi = (int)v, vi++, v = (Dwg_Version_Type)vi)

The other solutions here are not C backcompat, and quite large.

rurban
  • 4,025
  • 24
  • 27
1

Very Simple:

nextAvail = (StackID)(nextAvail + 1);
Rich
  • 6,470
  • 15
  • 32
  • 53
  • Hello! Please consider providing an explanation to your solution. See https://stackoverflow.com/help/how-to-answer for further help on answering questions. – Impurity Jun 18 '21 at 12:43
0

With respect to oprator++, $5.2.6/1 states- "The type of the operand shall be an arithmetic type or a pointer to a complete object type."

StackID does not fit the bill here. It is of enumeration type.

One option is like this

$5.7/1 - "For addition, either both operands shall have arithmetic or enumeration type, or one operand shall be a pointer to a completely defined object type and the other shall have integral or enumeration type."

enum Possibility {Yes, No, Maybe};

Possibility operator++(Possibility const& r){
   return Possibility(r + 1);   // convert r to integer, add 1, convert back to Enum
}

int main(){
   Possibility p = Yes;
   Possibility p1 = ++p;
}
Chubsdad
  • 24,777
  • 4
  • 73
  • 129