Concepts / Enumerator Scope and Qualification

Enumerator Scope and Qualification

A plain enum is an enumeration whose enumerators are entered into the same scope as the enum name, and which implicitly converts to integers.

  • Programming

Predicting What a Plain Enumerator Stores

Sometimes a program needs a fixed set of named choices, such as the colors it understands, and it is convenient for each choice to be usable as a plain name rather than a qualified one. C++ enumerations provide that tool. Before any rules are introduced, read the two lines below once and do not run them.

cpp

What do you think happens?

After the second line runs, what value does col hold?

  • 0

  • 1

  • 2

  • green

Reveal answer
Answer:

1

The name green is written with no enum name in front of it, and it is an enumerator of a plain enum. Plain enumerators implicitly convert to their integer value. Green is the second enumerator in the list, and counting starts at 0, so green has the value 1 and col receives 1. No cast is written.

The rest of this article explains the two rules that produce that result: where the names of a plain enum are visible, and how its integer values are counted. Keep the answer you chose in mind, because the explanation will show whether your prediction matched the rules.

What Makes an Enum Plain

Definition

A plain enum is an enumeration whose enumerators are entered into the same scope as the enum name, and which implicitly converts to integers.

A plain enum is created by writing the enum keyword, then a name, then a braced list of enumerators. The plain form is obtained from the scoped form by removing the word class from enum class. That single change trades explicit qualification and conversion for convenience. The scoped form requires you to write the enum name before an enumerator, while the plain form does not. The plain form also converts an enumerator to an integer automatically, while the scoped form does not.

So a plain enum makes two choices, and both are the looser setting. First, enumerator names do not have to be qualified. Second, enumerator values behave as integers without an explicit conversion. Those two properties are what the word plain refers to in this article.

Where Plain Enumerator Names Are Visible

The enumerators of a plain enum are placed in the same scope as the enum's own name. In the Color example, the name Color and the names red, green, and blue all belong to the scope where the enum is declared. No extra qualifier is needed to reach an enumerator from that scope. A name such as green can be written directly in the code that uses it.

Enclosing scopewhere the enum is declaredColorenum nameredenumeratorgreenenumeratorblueenumeratorcontainscontainscontainscontains
Which names can code in the same scope use without qualification, and how does the enum name relate to its enumerators?

The same placement that makes plain enums convenient also has a cost. Enumerator names share the surrounding scope with everything else declared there. This is part of why plain enums are described as less well behaved than their scoped counterparts. A name introduced by an enum can collide with a variable, a function, or another enumerator that already occupies the same scope.

cpp
Notes on the code
  • Color and Light both declare names in the same scope, and every enumerator of each is visible without a qualifier.
  • green is used directly, with no enum name in front of it.
  • Because the names are all in one scope, a second enum that also declared green would conflict with the first.

Counting Integer Values

The integer a plain enumerator converts to is determined by its position in the list. By default, integer values start at 0, and each additional enumerator has a value one greater than the enumerator before it. The first enumerator in the list has value 0, the next has value 1, and so on through the list in order.

Formula
red0green1blue2plus 1plus 1
When an enumerator has no explicit value, what integer does it produce, and how does each value follow from the one before it?
Worked example

Predicting the value of blue

Given enum Color { red, green, blue };, what integer does blue convert to?

  1. 1
    Identify the position

    The enumerators appear in the order red, green, blue. Blue is third in the list.

  2. 2
    Start counting at zero

    The first enumerator, red, has value 0.

  3. 3
    Add one for each step

    Green is one greater than red, so green is 1. Blue is one greater than green, so blue is 2.

Result

blue converts to 2.

An enumerator can also be given an explicit value. When it is, the enumerators after it continue counting from that value rather than from the default. The following generated example shows that behavior.

cpp
Output
m is 6 and h is 7
Notes on the code
  • low is given the explicit value 5.
  • medium has no explicit value, so it is one greater than low, which is 6.
  • high is one greater than medium, which is 7.

Tracing col Through the Assignment

Now trace the state of the program from the opening example. Before the assignment, col has no value that you can rely on. The assignment reads green, which is a plain enumerator, and the implicit conversion turns it into its integer value. The variable then holds that integer.

Before
enum Color { red, green, blue };
int col;

col is declared but has no assigned value

After
enum Color { red, green, blue };
int col = green;

col holds 1, the integer value of green

No cast appears anywhere in the assignment. The conversion happens because an enumerator appearing where an integer is needed is converted to its integer value. The stored value is the position-based value from the counting rule, so the only thing you need to know to predict col is the position of green in the list.

Plain Enum Versus enum class

Comparing the two forms makes the design choice clear. The plain form gives you convenience, and the scoped form gives you explicit qualification and no implicit conversion to int.

Unqualified namesgreen is written directlyImplicit intconversiongreen converts to 1Qualified namesthe enum name is writtenfirstNo implicitconversionan explicit cast is needed
What differs between a plain enum and an enum class in name visibility, required qualification, and implicit conversion to int?
PropertyPlain enumenum class
Declarationenum Color { red, green, blue };enum class Color { red, green, blue };
Enumerator namesVisible in the enclosing scope without qualificationRequire the enum name before the enumerator
Conversion to intImplicit; no cast writtenNot implicit; the source contrasts this with the plain form
Main trade-offConvenience, with names sharing the surrounding scopeExplicit qualification and conversion

Two properties separate a plain enum from its scoped counterpart.

Plain enums remain common in current code, and they have been part of C++ and C since the earliest days of both languages. When you read existing C++ code, you should expect to meet them, so understanding their two properties is practically useful even when you would choose the scoped form for new code.

Mistakes With Plain Enum Code

  • Common mistake

    Assuming a plain enumerator needs the enum name in front of it

    What it looks like
    int col = Color::green;
    Why it fails

    This is the qualified style of the scoped form. A plain enumerator is entered directly into the enclosing scope, so the bare name green is already visible.

    Do this instead

    Write int col = green; when the enum is plain.

  • Common mistake

    Counting the integer values from 1 instead of 0

    What it looks like

    Predicting that green is 2 in enum Color { red, green, blue };

    Why it fails

    By default, integer values start at 0, so the first enumerator is 0 and green is 1.

    Do this instead

    Count from zero in list order: red is 0, green is 1, blue is 2.

  • Common mistake

    Expecting a cast to be required for the conversion

    What it looks like

    Believing that int col = green; is an error and needs (int)green

    Why it fails

    A plain enumerator implicitly converts to its integer value, so no cast is written.

    Do this instead

    Read the assignment as storing the enumerator's integer value.

  • Common mistake

    Reusing an enumerator name in the same scope

    What it looks like

    Two plain enums in one scope both declaring green

    Why it fails

    Plain enumerator names share the surrounding scope, so the two declarations clash.

    Do this instead

    Use distinct enumerator names across the scope, or place the enums in separate scopes.

Practicing the Count

EASY

Given enum Shape { circle, square, triangle };, what integer value does the assignment int x = triangle; store in x? Work it out before checking the hints.

Hints
  • Identify the position of triangle in the list.
  • The first enumerator, circle, has value 0.
  • Each following enumerator adds one: square is 1 and triangle is 2.

Key Takeaways

  • A plain enum is an enumeration whose enumerators are entered into the same scope as the enum name, and which implicitly converts to integers.
  • The plain form is obtained by removing the class from enum class, which trades explicit qualification and conversion for convenience.
  • Plain enumerator names are visible in the enclosing scope without qualification, so they share that scope with every other name declared there.
  • By default, integer values start at 0 and increase by one for each enumerator in list order, and an assignment to an int stores that value without a cast.

Related concepts

More concepts starting with E