C++11 type trait to differentiate between enum class and regular enum
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 +
.
type trait with enum as specialisation
typename
and class
expect types. Http::Get
is (presumably) not a type, but a value, like any other constant (42
, 'A'
, false
etc.). And you obviously cannot pass a value when a type is expected.
The solution would be different depending on your use cases. For example:
#include <type_traits>
enum class Http {
Post,
Get,
};
template <auto T>
struct isGet : public std::false_type {};
template <>
struct isGet<Http::Get> : public std::true_type {};
Pass C++11 enum class as template while auto deducing its type
There are 3 approaches, none of them good.
First, you could wait for a later standard: a number of proposals to fix this problem have been made. I do not know if any made it into C++1y.
Second, macros.
Third, use a deduced type. This forces the enum value to be at best a constexpr
parameter.
The shorter answer is 'you cannot do what you ask, at least not cleanly'. The mess has been noted, and may one day be fixed.
In a type trait, why do people use enum rather than static const for the value?
A notable difference is in the fact that the following code compiles and links:
template<typename>
struct is_pointer { };
template<typename T>
struct is_pointer<T*> {
enum { value = true };
};
void f(const bool &b) { }
int main() {
f(is_pointer<void*>::value);
}
The following does not work instead (you get an undefined reference to value
):
template<typename>
struct is_pointer { };
template<typename T>
struct is_pointer<T*> {
static const bool value = true;
};
void f(const bool &b) { }
int main() {
f(is_pointer<void*>::value);
}
Of course, it doesn't work unless you add somewhere the following lines:
template<typename T>
const bool is_pointer<T*>::value;
That is because of [class.static.data]/3 (emphasis mine):
If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression ([expr.const]). The member shall still be defined in a namespace scope if it is odr-used ([basic.def.odr]) in the program and the namespace scope definition shall not contain an initializer. [...]
In other terms, static const bool value = true;
is a declaration, not a definition and you cannot odr-use value
.
On the other side, according with [dcl.enum/1] (emphasis mine):
An enumeration is a distinct type with named constants.
Those named constants can be const referenced as shown in the example above.
As a side note, something similar applies if you use static
constexpr
data members in C++11/14:
template<typename T>
struct is_pointer<T*> { static constexpr bool value = true; };
This doesn't work as well and that's how I discovered the subtle differences between them.
I found help here on SO getting some nice hints out of the answer I've been given.
References to the standard are a plus to better explain what's going on under the hood.
Note that a static
constexpr
data member declaration like the one above is also a definition since C++17. Therefore you won't have to define it anymore and you'll be able to odr-use it directly instead.
As mentioned in the comments (thanks to @Yakk that confirmed this) I'm also trying to explain how it happens that the above mentioned named constants bind to a const reference.
[expr.const/3] introduces the integral constant expression and mentions unscoped enum
s by saying that it's implicitly converted to a prvalue.
[dcl.init.ref/5] and [class.temporary/2] do the rest, for they rule on reference binding and temporaries.
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> {};
Type trait for enum member value
Use std::enable_if
:
template <typename T, typename = void>
struct has_none : std::false_type {};
template <typename T>
struct has_none<T, std::enable_if_t<T::None == T(0)>> : std::true_type {};
Ensure template parameter is an enum class
You can achieve it with:
template<typename T>
using is_class_enum = std::integral_constant<
bool,
std::is_enum<T>::value && !std::is_convertible<T, int>::value>;
Here a demo.
If you prefer using SFINAE, the same can be achieved with:
template<typename T, typename _ = void>
struct is_class_enum : std::false_type {
};
template<typename T>
struct is_class_enum <
T,
typename std::enable_if<std::is_enum<T>::value &&
!std::is_convertible<T, int>::value>::type> :
public std::true_type {
};
Related Topics
No == Operator Found While Comparing Structs in C++
Under What Circumstances Are C++ Destructors Not Going to Be Called
C++ Stl: Array VS Vector: Raw Element Accessing Performance
Inheriting Private Members in C++
C++ Implementing Timed Callback Function
Why Does Enable_If_T in Template Arguments Complains About Redefinitions
What Is the Rationale for Limitations on Pointer Arithmetic or Comparison
Which Is Faster/Preferred: Memset or for Loop to Zero Out an Array of Doubles
How to Typedef a Template Class
Recursive Lambda Functions in C++14
Difference Between String.H and Cstring
C++ Linking Error After Upgrading to MAC Os X 10.9/Xcode 5.0.1
Why Do We Use Std::Function in C++ Rather Than the Original C Function Pointer
Use of Typename Keyword with Template Function Parameters