Damon
Damon

Reputation: 70126

Rationale behind scope resolution in strongly typed enumerations

What is the rationale behind unconditionally requiring explicit scope resolution in strongly typed enumerations?

N2347 explains the difference to old-fashion enumerations which are absence of implicit conversion, ability to specify storage type, and no injection of names in the surrounding scope (as in C++03, which has that as a heritage of C).

In other words, writing enum E1 { a, b, c}; as in C++03 is similar to writing

const int a = 1; const int b = 2; const int c = 3;

whereas enum E1 class { a, b, c}; as in C++11 is much more similar to something like

namespace E1 { const int a = 1; const int b = 2; const int c = 3; }

(without introducing a namespace, and with defining an enum type in either case).

Now, I generally don't understand where there is an ambiguity, assuming one has for example code like the following (which won't compile):

enum class E1 { a, b, c };
enum class E2 { a, b, c }; // allowed now

void foo(E1 e) { ... }
void bar(E2 e) { ... }
void baz(int e) { ... }

foo(a);   // not ambigious: E1 expected, nothing but E1::a possible
bar(a);   // not ambigious: E2 expected, nothing but E2::a possible
baz(a);   // not ambigious: illegal - no name `a` in global scope

E1 x = a; // not ambigious: E1 expected, nothing but E1::a possible

I welcome (optional) explicit scope resolution in some cases to point out what's going on, but I do not understand why C++11 requires explicit scope resolution, even when there is no possible way of interpreting code in another way.

It would in my opinion be reasonable to expect that for example void foo(E1 e); has a meaning much more like void foo(using enum E1; E1 e); (my syntax is of course totally wrong, but you get the idea).

Taking the "classic" example of Color and Alertthat is also in N2347, one has red alert, and the color red, which may be different numeric constants, too. Without strong type guarantees, it is conceivable that one ends up using the alert numeric constant when one really wants to e.g. set a red color on a display. Or, with integer conversions and a lax function declaration, it is conceivable that someone ends up using something like yellow|red to get orange.

None of that is possible, so what exactly are we defending against?

Upvotes: 2

Views: 211

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473292

foo(a); // not ambigious: E1 expected, nothing but E1::a possible

The type of the expression must be known. And since the use of a as a free-standing expression is ambiguous, the use of a anywhere is ambiguous.

You don't want expressions to change their meaning depending on the context they're used in. 1 + 1 always means the same thing. 1 + t always means the same thing if you use the same t. Similarly, a should always mean the same thing no matter where it is used.

The only thing in C++ that allow for the deduction of the source type based on the context in which it is used is uniform initialization. And the standard explicitly states that a "braced-init-list" is not an expression. a is an expression, so it follows the expression rules.

Upvotes: 5

Related Questions