How to Determine If a Type Is a Scoped Enumeration Type

Is it possible to determine if a type is a scoped enumeration type?

I think testing if it is an enum and not implicitly convertible to the underlying type should do the trick.

template <typename T, bool B = std::is_enum<T>::value>
struct is_scoped_enum : std::false_type {};

template <typename T>
struct is_scoped_enum<T, true>
: std::integral_constant<bool,
!std::is_convertible<T, typename std::underlying_type<T>::type>::value> {};

Underlying type of a C++ enum in C++0x

I haven't read any C++0x stuff so I couldn't comment on that.

As for serializing, you don't need the switch when reading the enum back in - just cast it to the enum type.

However, I don't cast when writing to the stream. This is because I often like to write an operator<< for the enum so I can catch bad values being written, or I can then decide to write out a string instead.

enum color { red, green, blue };
color c = red;

// to serialize
archive << c; // Removed cast

// to deserialize
int i;
archive >> i;
c = (color)i; // Removed switch

Is enum class a class type in C++?

enum class is not a class definition - the combination of keywords is used to define a scoped enumeration, which is a completely separate entity from a class.

std::is_class correctly returns false here. If you use std::is_enum, it will return true.


From the Standard:

The enumeration type declared with an enum-key of only enum is an unscoped enumeration, and its enumerators are unscoped enumerators. The enum-keys enum class and enum struct are semantically equivalent; an enumeration type declared with one of these is a scoped enumeration, and its enumerators are scoped enumerators.

There is no mention of an enum class being a "class type" anywhere in the Standard.

What is the underlying type of a c++ enum?

From N4659 C++ 7.2/5:

For an enumeration whose underlying type is not fixed, the underlying type is an integral type that can represent all the enumerator values defined in the enumeration. If no integral type can represent all the enumerator values, the enumeration is ill-formed. It is implementation-defined which integral type is used as the underlying type except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0.

How to safely cast integral types to scoped enums

A common way to do that is to include in your enum an ending marker

enum class Colors : char
{
Red,
Green,
Blue,
Last_Element
}

Using this approach, when converting, you can check if the value you're using is less than the value of Last_Element.

Consider the following function:

template<typename T>
typename std::enable_if<std::is_enum<T>::value, bool>::type
IsValidEnumIntegral<T>(int integral)
{
return (int)(T::Last_Element) > integral;
}

and you could use it like so:

if(IsValidEnumIntegral<Colors>(2))
//do whatever

This would work for any enum you made with an element called Last_Element. You could go further to create a similar function to then auto-convert for you.

Note: This is not tested. I am not in a position to do so at the moment but I think this could work.

EDIT: This will only work if the enum in question uses a set of integers with no gaps for its elements. The function provided will also assume the enum does not contain negative integers, though a First_Element can easily be added to it.

Why does the scoped enum support operator ' ' by default?

If you are referring to the "usual arithmetic conversions", then yes they are done when the arguments are arithmetic or enumeration types. It's just that there is a special bullet there for scoped enums:

[expr]

11 Many binary operators that expect operands of arithmetic or
enumeration type cause conversions and yield result types in a similar
way. The purpose is to yield a common type, which is also the type of
the result. This pattern is called the usual arithmetic conversions,
which are defined as follows:

  • If either operand is of scoped enumeration type, no conversions are performed; if the other operand does not have the same type, the expression is ill-formed.

So this case is covered here. Since the two operands are of the same scoped enum type, they are just checked to hold the specific relation in the fashion one would expect.

Can an enum class variable take the full range of integer values?

[dcl.enum]

8 For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type.

Once the underlying type is fixed, any value in its range is a value of the enumeration. The enumerators are just named constants in that range.

There is some minutiae involved in determining when the underlying type is fixed, but it doesn't apply here. A scoped enumeration always has a fixed type (int if none is given explicitly).

scoped but semi-weakly typed enumerations

For the initialization there is not much you can do other than cast the value. Note that C++11's strongly typed enums are not meant to replace but rather complement the existing enums. If you want a weakly-typed enum, just don't use enum class.

For the comparison of a strongly-typed enum, you can declare the necessary operators and cast inside the implementation:

bool operator==( uint32_t lhs, SystemEvents rhs )
{
return static_cast< SystemEvent >( lhs ) == rhs;
}

Of course you need both directions:

bool operator==( SystemEvents lhs, uint32_t rhs );

And other operators like !=.

How to know underlying type of class enum?

Since C++ 11 you can use this:

  • std::underlying_type class template to know the underlying type of enum.

The doc says,

Defines a member typedef type of type that is the underlying type for the enumeration T.

So you should be able to do this:

#include <type_traits> //include this

FooEnum myEnum;
auto pointer = static_cast<std::underlying_type<FooEnum>::type*>(&myEnum);

In C++ 14 it has been a bit simplified (note there is no ::type):

auto pointer = static_cast<std::underlying_type_t<FooEnum>*>(&myEnum);

And finally since C++ 23 one can get value without explicit cast (docs):

auto value = std::to_underlying<FooEnum>(myEnum);


Related Topics



Leave a reply



Submit