30

As we know, incrementation and decrementation of enum in C++03 is illegal, because C++03 enum may not be continuous. But the C++11 standard introduced the new enum class construction, which, according to Wikipedia, is more type-safe because it isn’t built on any simple countable type. So now, if we have a bounded list of values of an enum, can we write something like

enum class Colors { Black, Blue, White };
// ...
Colors color = Colors::White;
color++;

and will it work correctly (e.g. incrementation of White will return Black and decrementation of Black will return White)?

If we can't write such code, do you know any behavior-like classes either from boost or from Qt that provide us this feature (correct in- and decrementing)?

Ivan Akulov
  • 4,323
  • 5
  • 37
  • 64
  • 1
    There's nothing to say that the values of class enums are contiguous. In this respect, they are the same as C++03 enums. – juanchopanza Mar 16 '13 at 15:18
  • possible duplicate of [Using enum in loops and value consistency](http://stackoverflow.com/questions/13971544/using-enum-in-loops-and-value-consistency) – Bo Persson Mar 16 '13 at 15:20
  • Added one more question; please read. – Ivan Akulov Mar 16 '13 at 15:29
  • 2
    @juanchopanza: C++03 enums most certainly inherit the C behavior that values are assigned sequentially. Each enum item without an initializer is the previous item + 1. If the first has no initializer, it will have the value zero. This is completely guaranteed and standard, and has been ever since there was a Standard. see Section 7.2p2 – Ben Voigt Mar 16 '13 at 15:33
  • 3
    Who told you that `enum class` isn't built on a base integral type? The only difference is that now you can optionally choose the base type yourself, instead of the compiler doing it in an implementation-dependent way. – Ben Voigt Mar 16 '13 at 15:38
  • @benvoigt what I mean is that you can still have jumps in values, as in c++03. – juanchopanza Mar 16 '13 at 15:45
  • @juanchopanza: You can explicitly assign values, but skips never happen implicitly. – Ben Voigt Mar 16 '13 at 18:19
  • @BenVoigt right, so there is no safe, generic way of incrementing enum values. – juanchopanza Mar 16 '13 at 19:01
  • 1
    The range of representable values for a enum is always continuous. Which values in that range are given explicit names and which are not is completely irrelevant. The fact that one can't use the above operators with enum objct has nothing to do with its continuity. – AnT stands with Russia Feb 02 '19 at 20:14

1 Answers1

36

will it work correctly

No. enums are not designed to "wrap around" in the way you describe by default.

And C++11's enum class doesn't guarantee contiguous values, the same as you describe for C++03's enum.

You can define the wrapping behavior for your particular enum though. This solution assumes that the values are contiguous, like the enum you described.

enum class Colors { Black, Blue, White, END_OF_LIST };

// Special behavior for ++Colors
Colors& operator++( Colors &c ) {
  using IntType = typename std::underlying_type<Colors>::type
  c = static_cast<Colors>( static_cast<IntType>(c) + 1 );
  if ( c == Colors::END_OF_LIST )
    c = static_cast<Colors>(0);
  return c;
}

// Special behavior for Colors++
Colors operator++( Colors &c, int ) {
  Colors result = c;
  ++c;
  return result;
}
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • How does this cope with non-contiguous values? – juanchopanza Mar 16 '13 at 15:28
  • @juanchopanza only in that we can observe that there are none. – Drew Dormann Mar 16 '13 at 15:30
  • So compiler accepts `color++` for `enum class` without implementing operators? – Ivan Akulov Mar 16 '13 at 15:30
  • @gxoptg no, there is a recursion going on. – juanchopanza Mar 16 '13 at 15:31
  • @DrewDormann added one more question in the end, please read :) – Ivan Akulov Mar 16 '13 at 15:34
  • @juanchopanza Thanks for the heads-up. Edited. Recursion removed. – Drew Dormann Mar 16 '13 at 15:37
  • 5
    Is there a way to implement `++` without casts? I can't think of any, but this implementation bothers me. Casts are usually indication of something wrong and there has to be a very good reason for their usage. I would expect the language to allow implementation of `++` to be achievable without forcing to a specific type. – SomeWittyUsername Mar 16 '13 at 15:47
  • @icepack I feel your pain. :) Perhaps you could consider casts as being "indicative of low-level code" instead of just "wrong". This is effectively emulating a construct that doesn't exist in C++. Something that's like an `enum`, but with a contiguous guarantee, and therefore with math ops permitted. – Drew Dormann Mar 16 '13 at 16:08
  • 1
    And what about non-contiguous `enum class`? Iteration over a range of allowed values is a very natural operation and I don't see a way to achieve it even with casts. – SomeWittyUsername Mar 16 '13 at 16:18
  • 3
    @icepack That's because there isn't. `enum`s are, for that reason, not a particularily useful construct in most situations. – Cubic Mar 16 '13 at 17:09
  • If you need a [state machine](http://en.wikipedia.org/wiki/Finite-state_machine) then just use a [state machine](http://www.boost.org/libs/msm/); trying to use an enum for something it's not intended for is simply counterproductive. – ildjarn Mar 19 '13 at 00:26
  • @SomeWittyUsername Yes, of course. Use switch; it's tremendously inflexible though, e.g: switch (c) { case Black: return Blue; case Blue: return White; etc... – cosimo193 Sep 29 '20 at 14:45
  • Is it possible to template this to work for _any_ enum class instead of just one particular `enum class`? If I have 10 different enum classes I would consider this prohibitively complex to maintain all of these `operator++()` overloads for incrementing each type of `enum class`. Note: I'm not so concerned with the wrap-around feature, just the bare increment feature. I'd like to make my increment cleaner [in my answer here](https://stackoverflow.com/a/69762682/4561887). – Gabriel Staples Oct 29 '21 at 19:28