6

I've read that you shouldn't trust on the underlying implementation of an enum on being either signed or unsigned. From this I have concluded that you should always cast the enum value to the type that it's being compared against. Like this:

enum MyEnum { MY_ENUM_VALUE = 0 };

int i = 1;
if (i > static_cast<int>(MY_ENUM_VALUE))
{
    // do stuff
}

unsigned int u = 2;
if (u > static_cast<unsigned int>(MY_ENUM_VALUE))
{
    // do more stuff
}

Is this the best practice?

Edit: Does the situation change if the enum is anonymous?

khuttun
  • 683
  • 1
  • 4
  • 14

3 Answers3

8

An enum is an integer so you can compare it against any other integer, and even floats. The compiler will automatically convert both integers to the largest, or the enum to a double before the compare.

Now, if your enumeration is not supposed to represent a number per se, you may want to consider creating a class instead:

enum class some_name { MY_ENUM_VALUE, ... };

int i;
if(i == static_cast<int>(some_name::MY_ENUM_VALUE))
{
    ...
}

In that case you need a cast because an enum class is not viewed as an integer by default. This helps quite a bit to avoid bugs in case you were to misuse an enum value...


Update: also, you can now specify the type of integer of an enum. This was available in older compilers too, but it was often not working quite right (in my own experience).

enum class some_name : uint8_t { ... };

That means the enumeration uses uint8_t to store those values. Practical if you are using enumeration values in a structure used to send data over a network or save in a binary file where you need to know the exact size of the data.

When not specified, the type defaults to int.


As brought up by others, if the point of using enum is just to declare numbers, then using constexpr is probably better.

constexpr int MY_CONSTANT_VALUE = 0;

This has the same effect, only the type of MY_CONSTANT_VALUE is now an int. You could go a little further and use typedef as in:

typedef int my_type_t;
constexpr my_type_t MY_CONSTANT_VALUE = 0;

I often use enum even if I'm to use a single value when the value is not generally considered an integer. There is no set in stone rule in this case.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • ***if your enumeration is not supposed to represent a number per se**, you may want to consider creating a class instead* How to understand the text in bold in the right way? Is not the `MY_ENUM_VALUE` in your example equivalent to `0`, even though you didn't explicitly define that. – John Dec 05 '22 at 02:35
  • ***An enum is an integer so you can compare it against any other integer, and even floats.*** Is it still right with the strongly typed `enum`? – John Dec 05 '22 at 06:46
  • @John Internally, `enum class ...` are still just integers. However, trying to do `i == some_name::MY_ENUM_VALUE` fails with a warning (or an error if you use `-Werror`). You can use the `static_cast` to convert between those types. However, by default (without a cast) it allows you to be more sure you are using the correct type at the correct location (i.e. calling a function which expects a `some_name` won't compile when you try to pass `0` instead of `some_name::MY_ENUM_VALUE`—you could cast the `0`, however.) I think it makes your code more secure/less likely to have a bug. – Alexis Wilke Dec 06 '22 at 16:51
3

Short answer: Yes

enum is signed int type, but they get implicitly cast into unsigned int. Your compiler might give a warning without explicit casting, but its still very commonly used. however you should explicitly cast to make it clear to maintainers.

And of course, explicit cast will be must when its a strongly typed enum.

Community
  • 1
  • 1
Kaidul
  • 15,409
  • 15
  • 81
  • 150
  • 1
    "enum is signed int type, but they get implicitly cast into unsigned int" This is wrong. The underlying type of an unscoped `enum` is an integer type that can fit the largest enumerator. This means that if it is bigger than `INT_MAX` it can be `unsigned int`, `long`, `unsigned long`, etc. And it is implicitly cast to the smallest integer type that can fit the largest enumerator (which isn't always `unsigned int`). – Simple Sep 11 '14 at 08:55
0

Best practice is not to write

int i = 1;
if (i > static_cast<int>(MY_ENUM_VALUE))
{
    // do stuff
}

instead write

MyEnumValue i = MY_ENUM_VALUE ;
...
if ( i > MY_ENUM_VALUE ) {..}

But if - as in your example - you only have one value in your enum it is better to declare it as a constant instead of an enum.

AndersK
  • 35,813
  • 6
  • 60
  • 86
  • I agree that this situation mostly arises when a value that should have been defined as a constant is defined in an enum. – khuttun Sep 11 '14 at 08:45