The type trait “std::is_scoped_enum<T>::value” was introduced in C++23 to check whether the type “T” is a scoped enum type. Another way to use this is std::is_scoped_enum_v<T>.
Before getting into this trait in detail, let us briefly recap the differences between unscoped and scoped enums.
Unscoped Enums
Unscoped enums are the old-style enums. Look at the following example:
The enum keyword is usually followed by a name (optional) followed by a list of symbols within braces. Line 11 is a special case where the underlying type of the enum elements is explicitly stated. When this is not given, the compiler is free to choose any underlying integral type.
The above example shows that such enums are unsafe because of implicit conversion to integral type. Also note that the enum elements are visible at the scope where the enums are defined, and there is no need to use the scope resolution operator to access them. This can easily lead to problems later on.
Here is the output from the program:
Unscoped enums are not recommended in newer programs.
Scoped Enums
Scoped enums were introduced in C++11. In contrast to unstopped enums, these enums are type safe without implicit conversion to the underlying type. The following example illustrates this:
The key difference is the use of the keyword class or struct as part of the enum definition. Although either of them can be used with the same effect, the preferred convention is to use class.
Note that we need to use explicit type casting to convert the enum elements to the underlying integral type. In addition, we have to use the scope resolution operator to access the members.
If we uncomment lines 39 – 41, the program won’t compile!
std::is_scoped_enum<T>
Now we return to our type trait is_scoped_enum<T>::value, where “T” is a type name. Its abbreviated form is is_scoped_enum_v<T>. This returns “true” if “T” happens to be a scoped enum, else it returns “false”. Here is an example:
The corresponding output is:
As expected, the trait correctly differentiates between scoped enums and other types.
OK, how do we use this feature in a program? One usage scenario is to take advantage of C++23 “Concepts” to define constraints on function argument types. The following example shows how to do this:
The “foo()” function is defined to accept argument of any scoped enum type by constraining the type. That way, the compiler can easily differentiate between valid and valid argument types, especially with template functions.
The example C++ program can be downloaded here. I used Visual Studio Professional 2022 Ver 17.6.5 to build this program.
Have a nice weekend!
Recent Comments