28

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?

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
Sam
  • 616
  • 7
  • 19
  • 1
    The real problem is that you're using `va_arg`s instead of a `std::initializer_list` and/or variadic templates. –  Mar 23 '13 at 11:50
  • Thanks for the tip but I have the va_list because I'm working with a C interface. – Sam Mar 23 '13 at 13:08
  • @Sam: Is my answer solving your problem? – Andy Prowl Mar 24 '13 at 17:14
  • @AndyProwl: Perfect answer, just what I was looking for! – Sam Mar 25 '13 at 20:32
  • Possible duplicate of [Is it possible to determine if a type is a scoped enumeration type?](https://stackoverflow.com/questions/10724783/is-it-possible-to-determine-if-a-type-is-a-scoped-enumeration-type) – underscore_d Jun 10 '18 at 09:30

2 Answers2

45

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 +.

Community
  • 1
  • 1
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 4
    +1, but there is one cave-at: It only works as long as the author of some enum class `E` has **not** defined his own `operator+(int,E)`. Fix it by adding a `void dummy(int)` and use `decltype(dummy(std::declval()))`. – Daniel Frey Mar 23 '13 at 11:52
  • 1
    @DanielFrey: Good point. Actually I could use a different operator, such as `^`, to make it less likely to interfere with a user-defined operator overload – Andy Prowl Mar 23 '13 at 11:55
  • 1
    @AndyProwl: or call a function that takes an `int`. –  Mar 23 '13 at 11:56
  • 1
    Test the conversion to `int` as, as far as I know, it can not be provided by the user. – Daniel Frey Mar 23 '13 at 11:56
  • @DanielFrey: You're right, that makes the whole thing much simpler actually. – Andy Prowl Mar 23 '13 at 12:00
  • @Fanael: Good suggestion, thank you. Actually, it turns out `std::is_convertible<>` does the job, so that a simple alias template is enough to define the trait. – Andy Prowl Mar 23 '13 at 12:01
  • 1
    @AndyProwl: Now do one last edit and derive from [`std::integral_constant`](http://en.cppreference.com/w/cpp/types/integral_constant) instead of this 90's `static const bool value = ...`-thingy. ;) – Daniel Frey Mar 23 '13 at 12:05
1

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
Bart
  • 1,405
  • 6
  • 32