Language[lang]
C++ enum and enum class: declare, convert, switch
- Section
- Language [lang]
- Standard
- C++11
- Compilers
- MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
- Published
- Length
- 7 min read
In this answer8 clauses
Abstract
A C++ enum is a type whose values are a fixed list of names, each a whole number counted from 0 unless you set one. Write enum class in new code: its names stay inside the type, as Status::Shipped, and it becomes an int only when you ask, with static_cast. Turn a value into its name with a to_string function built on a switch with no default, so GCC and Clang warn when a name has no case.

A C++ enum is a type you define whose values are a fixed list of names, such as Pending, Shipped and Delivered. Behind each name is a whole number, counted from 0 unless you set one. Write enum class in new code: its names stay inside the type (Status::Shipped), and it becomes an int only through a cast.
An order’s status, in a whole program:
#include <iostream>
enum class Status { Pending, Shipped, Delivered };
int main() {
Status status = Status::Shipped;
if (status == Status::Shipped) {
std::cout << "On its way\n";
}
}— end example]
It printed:
On its way— end example]
enum class arrived in C++11; Since When’s C++11 table lists the first GCC, Clang and MSVC releases with it. The samples were built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++): as C++17 on all four, and as C++11 on GCC and Clang (libc++). All four printed the same unless a sentence says otherwise. using enum needs C++20, and std::to_underlying C++23.
What numbers are behind the names?
The first name is 0 and each next name is one more than the one before, unless you give a name its own value with = ([dcl.enum]). A name after a given value counts on from it, so Created is 201:
enum class Status { Pending, Shipped, Delivered };
enum class HttpCode { Ok = 200, Created, NotFound = 404 };
// ...
std::cout << static_cast<int>(Status::Pending) << ' '
<< static_cast<int>(Status::Shipped) << ' '
<< static_cast<int>(Status::Delivered) << '\n';
// 0 1 2
std::cout << static_cast<int>(HttpCode::Ok) << ' '
<< static_cast<int>(HttpCode::Created) << ' '
<< static_cast<int>(HttpCode::NotFound) << '\n';
// 200 201 404
Status s{}; // braces: the value 0, which is Pending
std::cout << std::boolalpha << (s == Status::Pending) << '\n';
// true— end example]
All four printed the numbers in the comments. Give values when the numbers mean something outside your program, as HTTP status codes do; otherwise let the compiler count.
enum vs enum class: what is the difference?
A plain enum puts its names into the surrounding scope, the namespace or block it is declared in, and turns into an int by itself. An enum class, also called a scoped enum, keeps its names inside the type and does neither (Enumeration declaration on cppreference). With the plain kind, Blue needs no Color:: and Green lands in an int with no cast:
#include <iostream>
enum Color { Red, Green, Blue };
int main() {
int n = Green; // no cast: n is 1
Color c = Blue; // no Color:: in front of Blue
std::cout << n << '\n'; // 1
std::cout << c << '\n'; // 2: c turns into an int here too
}— end example]
It printed 1 and 2: even std::cout received the color as its number. Because the names are loose in the scope, two plain enums there cannot both have a Red:
enum Color { Red, Green };
enum Light { Red, Amber }; // error: Red is already taken— end example]
Clang stopped at the second one:
error: redefinition of enumerator 'Red'— end example]
An enumerator is the standard’s word for one of the names; GCC and MSVC refused the second Red too. An enum class gives each type its own names, and it will not hand its number to an int without being asked:
int n = Status::Shipped; // error: no silent conversion to int— end example]
GCC said:
error: cannot convert 'Status' to 'int' in initialization— end example]

| Question | Plain enum |
enum class |
|---|---|---|
| Where the names live | the surrounding scope | inside the type: Status::Shipped |
Turns into an int |
by itself | only when asked: static_cast |
| Type behind it by default | one the compiler picks to fit the values | int |
How do you convert an enum to an int and back?
Use static_cast<int>(status) for the number and static_cast<Status>(n) to turn a number back into a Status; a cast is a conversion you write out, and static_cast is the one for this. Since C++23, std::to_underlying from <utility> gives the number in the enum’s underlying type, the integer type behind it, without you naming that type:
Status status = Status::Shipped;
int n = static_cast<int>(status); // enum to int
std::cout << n << '\n'; // 1
int input = 2;
if (input >= 0 && input <= 2) { // a name has this number
Status back = static_cast<Status>(input); // int to enum
if (back == Status::Delivered) {
std::cout << "Delivered\n";
}
}
std::cout << std::to_underlying(status) << '\n'; // C++23: 1
Status odd = static_cast<Status>(7); // compiles: no name is 7
std::cout << static_cast<int>(odd) << '\n'; // 7— end example]
Built as C++23, all four printed 1, Delivered, 1 and 7. As C++17 the std::to_underlying line compiled on none of them, so use static_cast there. Even a plain enum needs the cast in this direction: no number turns into an enum by itself.
How do you choose the type behind an enum?
Write the type after a colon; without one, an enum class uses int:
enum class Status { Pending, Shipped, Delivered };
enum class Level : std::uint8_t { Low, Medium, High };— end example]
On these compilers sizeof(Level) printed 1 and sizeof(Status) 4, the size of an int. std::uint8_t, an 8-bit unsigned integer type from <cstdint>, is worth writing only when the size matters. It is unsigned char on these compilers, so std::cout prints a Level’s number as a character; cast it with static_cast<int> to print the number. A plain enum takes a type the same way: enum Color : int.
How do you use an enum in a switch?
Write one case per name and no default: then GCC and Clang can warn when a name has no case, which is how you find every switch to update after you add a name. This switch turns each Status into its name:
const char* to_string(Status s) {
switch (s) {
case Status::Pending: return "Pending";
case Status::Shipped: return "Shipped";
case Status::Delivered: return "Delivered";
}
return "unknown"; // only a number with no name gets here
}— end example]
It printed Pending, Shipped and Delivered for the three names. Keep the return after the switch even though every name has a case: without it, GCC and MSVC warn that the function might not return a value.
With the Delivered case left out, GCC warned with -Wall, and Clang even with no warning options: enumeration value 'Delivered' not handled in switch. MSVC printed nothing at /W4: its warning for this, C4062, is off by default, and /w14062 turns it on. A default label silences GCC’s -Wswitch, which is why this switch has none; its stricter -Wswitch-enum warns even with one.
How do you convert an enum to a string?
C++ has no function that takes an enum value and returns its name, so write one like to_string above: a switch that returns each name. C++26 adds static reflection, with which one function can name the values of every enum. std:: printed Shipped. To print a Status with << itself, overload operator<< for it and call to_string inside.
What does using enum do?
Since C++20, using enum Status; inside a function lets you write Shipped instead of Status::Shipped in that function; Since When’s C++20 table has the first releases with it:
const char* to_string(Status s) {
using enum Status; // C++20: Pending instead of Status::Pending
switch (s) {
case Pending: return "Pending";
case Shipped: return "Shipped";
case Delivered: return "Delivered";
}
return "unknown";
}— end example]
Built as C++20, all four printed the same three names. Outside the function, the names still need Status::.
An enum checklist
- Write
enum class, unless you maintain code built on plain enums. - Give a type after the colon only when the size matters.
- Leave
defaultout of a switch over an enum, so GCC and Clang can name a missing case. - Convert with
static_cast, and check a number before you cast it to an enum. - Print the names with a
to_stringfunction built on a switch.
The Language section has the other answers on the core language.
Questions
What is the default value of an enum in C++?
An enum set with empty braces, as in Status s{};, holds the number 0, which is the first name when that name has no value of its own. A local variable declared with no braces, Status s;, is not set to anything, so give it a value before you read it.
How do you convert a string to an enum in C++?
C++ has no function for it. Write a function that compares the text with each name in turn and returns the matching value, such as Status::Shipped for “Shipped”, and decide what it returns when the text matches no name.
Can you forward declare an enum in C++?
Yes. enum class Status; declares the type with an int behind it, and a plain enum can be declared the same way only with its type given, as in enum Color : int;. Both compiled on GCC, Clang and MSVC. The names, such as Status::Shipped, can be used only after the full definition.
What is the size of an enum in C++?
The size of the integer type behind it. An enum class uses int unless you name another type after a colon: on GCC, Clang and MSVC, an enum class with no type took 4 bytes and enum class Level : std::uint8_t took 1. A plain enum with no type given uses an integer type the compiler picks to hold its values, no larger than int unless a value needs it.
How do you get the number of values in an enum?
Up to C++23 there is no built-in count. The usual trick is a last name such as Count after the real ones: when no name is given a value of its own, the number behind Count equals the number of names before it. A switch with no default then needs a case for Count too, or GCC and Clang warn that it is not handled.
Can an enum class have member functions?
No. Neither an enum class nor a plain enum can have member functions. Write ordinary functions that take one instead, such as a to_string(Status) built on a switch.
Sources
- Enumeration declarationcppreference.com
- [dcl.enum] Enumeration declarationsC++ standard working draft
- std::to_underlyingcppreference.com
- std::underlying_typecppreference.com
- P2996R13 Reflection for C++26WG21
- Options to Request or Suppress Warnings (-Wswitch)GCC, the GNU Compiler Collection
- Compiler Warning (level 4) C4062Microsoft Learn