(upbeat music)
Narrator: In this video, we'll look at the structure
or syntax of an enumeration.
If you've ever seen an enumeration before,
you may be confused by the structure shown here.
That's because many elements of an enumeration are optional.
So often you'll see enumerations without names,
types, or attributes.
You'll notice that we have also omitted attributes
from this structure.
That's because attributes are a separate topic altogether
and apply to more than just enumerations.
In this section we'll only be covering
the basics of enumerations
so topics such as attributes won't be included.
With that being said, let's take a look
at the elements of an enumeration that we'll be using.
First, we have the enumeration key.
This defines the beginning of the enumeration
as well as its scope.
We'll cover the different types of enumeration scopes
in detail soon,
but for now just know that the enumeration
can be either scoped or unscoped.
Regardless of the scope,
an enumeration will always start with the key word enum.
The next part of an enumeration is its name.
Naming is optional for unscoped enumerations,
but can be useful for imposing type restrictions
on variables that you want to be of an enumeration type.
This is similar to what we saw in the last video
where we imposed a type restriction on the state variable
by declaring it as a state enumeration type.
If we had not named the state enumeration,
we would not have been able to do that
and we'll see soon why this is the case.
Next we have a colon followed by a type specifier.
The specified type serves as the fixed underlying type
of the enumeration's enumerators.
Specifying the underlying type is optional,
but can be used to reduce the amount of space
an enumeration takes up in memory
or to increase the accuracy of any calculations
involving the enumerator values.
The last part of the enumeration is a set of curly braces
and this is the enumeration's enumerator list.
This is where we define the names and integral values
of the enumerators belonging to the enumeration.
This can be done explicitly or implicitly.
That's it, now let's look at a simple enumeration
to see these elements in action.
This is about as simple of an enumeration as we can get.
It has no name, no fixed underlying type,
and has three enumerators, red, green, and blue.
You'll notice that none of the enumerators
have been initialized.
This is because the compiler could do this for us.
If the first enumerator has no initializer,
the compiler will assign it a value of zero.
For all other enumerators without initializers,
the compiler will assign the value
of the previous enumerator plus one.
This is known as implicit initialization.
Of course we might not always want the integral values
assigned by the compiler
so we can always explicitly assign them ourselves.
In some cases, we may want to assign certain enumerators
specific values and not care what the others are assigned.
In cases like these, we can explicitly assign
certain enumerators the values we want
and have the compiler
implicitly assign values to the others.
This is known as explicit/implicit initialization.
Now let's take a look at the enumeration's
underlying type in more detail.
If the underlying type of an enumeration is not fixed,
the compiler assigns the first integral type
that it's able to hold the enumeration's entire value range.
These integral types are listed on the right in rank order
with signed given preference over unsigned.
The sizes or widths of these integral types
depends on the system being used,
but for this example,
we'll use the C++ minimum standard
which are reflected by the values listed.
Now with this knowledge,
can you tell what underlying type
the compiler will assign to our simple enumeration?
It might help to look at the minimum binary representation
of our enumerator values
assuming a signed bit is used.
We can see that each of the enumerators
can be represented by three bits
and so the first integral type
that is able to hold all three bits is int
since it has a width of 16 bits.
Now what if we explicitly initialize the enumerator blue
with the value -32769?
Obviously the underlying type will need to be signed
since it must be able to hold negative values,
but can we tell whether it will be an int,
a long, or a long long?
Let's take a look at the minimum binary representation
of our enumerator values again.
The ennumerator's red and green
require three bit representation
and blue a 32 bit representation.
Because the underlying type
must be able to hold all the enumerator values,
the compiler will base its selection
on the largest required bit representation.
In this case, blue, and so the compiler
will assign the enumeration the underlying type of long.
In some cases, we may want to fix an enumeration's
underlying type for the purposes of saving memory
or increasing the accuracy of calculations
involving enumerator values.
This can be accomplished by placing a colon
and the fixed underlying data type
directly before the enumerator's enumeration list.
C++ has types such as uint8, uint16 and so forth
that allow us to specify the exact number
of bits that we require.
If we try to fix an underlying type
that cannot hold the enumeration's entire range,
the compiler will throw an error.
Now let's look at the naming of an enumeration
in more detail.
Nameless enumerations are often referred
to as anonymous enumerations.
Because anonymous enumerations have no name,
it's not possible to declare variables
of that enumeration type.
The best we can do is declare a variable
as the underlying type of the enumeration,
but this means that while the variable
can be assigned to the enumerators,
it can also be assigned any value
belonging to the underlying type.
Because of this, we say that anonymous enumerations
provide no type safety.
In the case of the anonymous enumeration
containing the enumerators red, green, and blue,
if we want the variable my_color
to be assigned one of these enumerators,
we must declare it as the underlying type
of the enumeration, in this case int,
but this means that while it can be assigned red,
green and blue, it can also be assigned any integer value.
Oftentimes we want a variable's assignment
to be restricted to only the enumerators of an enumeration.
As we saw earlier with the rocket launch example,
this can be quite important
and so to accomplish this,
we can give an enumeration a meaningful name
that represents its enumerators.
By providing a name, we're defining the enumeration
as a unique type that can only assume its enumerator values.
Because of this, we say that the enumeration
provides type safety.
As good design practice, the name given to an enumeration
should form an is a relationship with its enumerators.
In this case, color is a good name for the enumeration
since red is a color, green is a color, and blue is a color.
By giving the enumeration the name color,
we have defined a unique type of that name
which can only assume the values of red, green, and blue.
As such, we can declare the variable my_color
as a color type,
thus restricting its assignment to only red, green or blue.
And that's it.
Almost everything you need to know
about enumeration structure.
The only thing that's left to cover are their scopes
and all the things we can do with them.
Now let's head over to the next video
to learn what unscoped enumerations are
and how we can use them.