Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 type trait to differentiate between enum class and regular enum

I'm writing a promotion template alias similar to boost::promote but for C++11. The purpose of this is to avoid warnings when retrieving arguments from varidic functions. e.g.

template <typename T> std::vector<T> MakeArgVectorV(int aArgCount, va_list aArgList) {     std::vector<T> args;     while (aArgCount > 0)     {         args.push_back(static_cast<T>(va_arg(aArgList, Promote<T>)));         --aArgCount;     }     return args; } 

The Promote template alias promotes the type following the default argument promotion for variadic arguments: 1) An integer that's smaller than an int is promoted to int 2) A float is promoted to double

My problem is that a standard C++ enum can be promoted but a C++11 enum class is not promoted (compiler does not generate a warning). I want Promote to work with a regular enum but ignore a C++11 enum class.

How can I tell the difference between an enum class and an enum in my Promote template alias?

like image 389
Sam Avatar asked Mar 23 '13 11:03

Sam


People also ask

What is the difference between a class enum and a regular enum?

An enum can, just like a class , have attributes and methods. The only difference is that enum constants are public , static and final (unchangeable - cannot be overridden). An enum cannot be used to create objects, and it cannot extend other classes (but it can implement interfaces).

What is the difference between enum and enum class in C++?

An enum just spills its contents into the enclosing scope, and is basically a const static integer. This means that the first element of any default enum is the same using the == operator. Enum classes have their own scope, and don't pollute the namespace that they are in.

What is underlying type of enum?

Each enum type has a corresponding integral type called the underlying type of the enum type. This underlying type shall be able to represent all the enumerator values defined in the enumeration. If the enum_base is present, it explicitly declares the underlying type.

What is underlying type in C?

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.


2 Answers

Here is a possible solution:

#include <type_traits>  template<typename E> using is_scoped_enum = std::integral_constant<     bool,     std::is_enum<E>::value && !std::is_convertible<E, int>::value>; 

The solution exploits a difference in behavior between scoped and unscoped enumerations specified in Paragraph 7.2/9 of the C++11 Standard:

The value of an enumerator or an object of an unscoped enumeration type is converted to an integer by integral promotion (4.5). [...] Note that this implicit enum to int conversion is not provided for a scoped enumeration. [...]

Here is a demonstration of how you would use it:

enum class E1 { }; enum E2 { }; struct X { };  int main() {     // Will not fire     static_assert(is_scoped_enum<E1>::value, "Ouch!");      // Will fire     static_assert(is_scoped_enum<E2>::value, "Ouch!");      // Will fire     static_assert(is_scoped_enum<X>::value, "Ouch!"); } 

And here is a live example.

ACKNOWLEDGEMENTS:

Thanks to Daniel Frey for pointing out that my previous approach would only work as long as there is no user-defined overload of operator +.

like image 171
Andy Prowl Avatar answered Oct 06 '22 10:10

Andy Prowl


It appears that as of C++23 a similar solution to the one provided by @AndyProwl will be available from type_traits

#include <type_traits>  enum E { a, b }; enum class Es { x, y, z };  std::is_scoped_enum_v<E>;  // False std::is_scoped_enum_v<Es>; // True 
like image 22
Bart Avatar answered Oct 06 '22 09:10

Bart