2

The C++ Coding guidelines, Enum.3: Prefer class enums over “plain” enums contains following example:

void Print_color(int color);

enum Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
enum Product_info { red = 0, purple = 1, blue = 2 };

Web_color webby = Web_color::blue;

// Clearly at least one of these calls is buggy.
Print_color(webby);
Print_color(Product_info::blue);

However this example doesn't compile (see https://godbolt.org/z/vsYTKPv8x). I do understand that a scoped enum would not pollute the global namespace, and will not as readily convert to integers, but the example looks like it is trying to show some kind of redeclaration problem for red and blue?

This is also related to the related entry in AUTOSAR C++ Coding Guidelines where Rule A7-2-3 states "Enumerations shall be declared as scoped enum classes." with the rationale:

If unscoped enumeration enum is declared in a global scope, then its values can redeclare constants declared with the same identifier in the global scope. This may lead to developer’s confusion.

Using enum-class as enumeration encloses its enumerators in its inner scope and prevent redeclaring identifiers from outer scope.

Note that enum class enumerators disallow implicit conversion to numeric values.

The first statement is hard to parse (which same identifier?), but it mentions "redeclare" so what is the underlying problem here (could it be related to Redeclare variable inside enum but the first line should be "If unscoped enumeration enum is declared in a global scope class")

8
  • 1
    Clearly both are buggy, enums are not ints at least no semantically, so both are semantically not recommended. In other words, Print_Color should never be declared using an int argument and a clear misuse of the C++ type system. Note : the compiler is your friend "mean what you say and say what you mean". Commented Oct 30 at 16:13
  • 1
    This is all intentional. They are illustrating that "red" can describe two completely different things, and that enum classes can prevent confusion regarding this. Commented Oct 30 at 16:16
  • @DrewDormann That's a better summary yes :) Commented Oct 30 at 16:19
  • 1
    Note that the issue here is not about values, it's about names. There's no problem having multiple enumerators with the same value. Commented Oct 30 at 18:43
  • This is a fine example why C and C++ are not safe by themselves, but need to be made safe by mandatory rules. Commented Oct 30 at 18:51

3 Answers 3

7

Can enum values be redeclared?

No.

the example looks like it is trying to show some kind of redeclaration problem for red and blue?

No.

The example is showing you that enums convert to int too readily, and class enums can be used to expose unclear, bad code such as:

void Print_color(int color);
Sign up to request clarification or add additional context in comments.

Comments

6

Example in Enum.3: Prefer class enums over “plain” enums is not polished properly.

Here is an improved version:

void Print_color(int color);

struct Web_color {
    enum type { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
};
struct Product_info {
    enum type { red = 0, purple = 1, blue = 2 };
};

constexpr Web_color::type webby = Web_color::blue;

void problemDemo() {
    // Clearly at least one of these calls is buggy.
    Print_color(webby);
    Print_color(Product_info::blue);
}

https://godbolt.org/z/38W9fcYba

Note that the first call Print_color(webby); uses value 0x0000FF to represent blue. Second call Print_color(Product_info::blue); uses value 2 to represent the same color. In both cases an implicit conversion to int is done.

You have two enums which represent the same color in different ways, and Print_color in one of these calls can't work correctly.

If these calls are far apart, this bug will be very hard to spot. Trying to fix the behavior of Print_color in one place will break the other use case. Actual problem is in silent conversion of enums of different types to int. Use of enum class will prevent this.

Comments

2

1. The scope

I must admit that I wasn't aware that unscoped enum values can be accessed like for scoped enums as
Web_color webby = Web_color::blue;
Print_color(Product_info::blue);

Coming from the language C (thats where the unscoped enums come from and how they still behave) there is nothing like a scope as ClassOrNamespace::Whatever, you only have a single identifier which is the enumeration value:

The above two code excerpts are identical to
Web_color webby = blue;
Print_color(blue);

When you view the problem like this then it should be obvious why the compiler complains: There is only one global definition of the identifier blue and the second attempt to declare it will fail.

A redeclaration of the same symbol is not possible in C++ (except forward declarations, which is in my opionion a different topic).
Lets have a look on a slightly different example:

int i = 10;
int i = 12; // error

In some cases it is only possible to hide a declaration of an outer scope by a redefinition in an inner scope:

int i = 10;
std::cout << i << "\n";
{
    int i = 12; // OK, but you may get a warning
    std::cout << i << "\n";
}

prints

10
12

But as unscoped enums are on the same (global) level it is consistent that this produces as well a compiler error.

2. The implicit conversion

Yes, any unscoped (C) enum can be implicitly converted to int, which is a completely different source of problems and not related to the scope problem.
Whether the implicit conversion is a problem or not depends on the intended expectation:
If it is expected that Print_color shall be able to print the value of any color enumeration (or any value of any other enum) then the code does exactly this.
If it is expected that Print_color prints red, green or blue (or red, purple or blue) based on the numeric input value then you are right with

Clearly at least one of these calls is buggy.

The scoped enums demand to make the cast to its underlying type explicit, so on the calls to Print_color it would be more obvious what happens and thus what possible side effects might arise:

Print_color(webby); -> Compiler error, webby is not an int
Print_color(static_cast<int>(webby)); -> Compiles, but this call should raise the question why webby needs to be converted and isn't taken as direct enumeration type and thus may help to prevent the later logic error when using it with Product_info::blue (under the assumption that Print_color was intended for Web_color).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.