The obvious thing is to annotate your enum:
// generic code
#include <algorithm>
template <typename T>
struct enum_traits {};
template<typename T, size_t N>
T *endof(T (&ra)[N]) {
return ra + N;
}
template<typename T, typename ValType>
T check(ValType v) {
typedef enum_traits<T> traits;
const T *first = traits::enumerators;
const T *last = endof(traits::enumerators);
if (traits::sorted) { // probably premature optimization
if (std::binary_search(first, last, v)) return T(v);
} else if (std::find(first, last, v) != last) {
return T(v);
}
throw "exception";
}
// "enhanced" definition of enum
enum e {
x = 1,
y = 4,
z = 10,
};
template<>
struct enum_traits<e> {
static const e enumerators[];
static const bool sorted = true;
};
// must appear in only one TU,
// so if the above is in a header then it will need the array size
const e enum_traits<e>::enumerators[] = {x, y, z};
// usage
int main() {
e good = check<e>(1);
e bad = check<e>(2);
}
You need the array to be kept up to date with e
, which is a nuisance if you're not the author of e
. As Sjoerd says, it can probably be automated with any decent build system.
In any case, you're up against 7.2/6:
For an enumeration where emin is the
smallest enumerator and emax is the
largest, the values of the enumeration
are the values of the underlying type
in the range bmin to bmax, where bmin
and bmax are, respectively, the
smallest and largest values of the
smallest bit-field that can store emin
and emax. It is possible to define an
enumeration that has values not
defined by any of its enumerators.
So if you aren't the author of e
, you may or may not have a guarantee that valid values of e
actually appear in its definition.
From n3290, 5.2.9 Static cast [expr.static.cast]:
10 A value of integral or enumeration type can be explicitly converted
to an enumeration type. The value is unchanged if the original value
is within the range of the enumeration values (7.2). Otherwise, the
resulting value is unspecified (and might not be in that range). [...]
Enumeration type comprises both those types that are declared with enum
and those that are declared with enum class
or enum struct
, which the Standard calls respectively unscoped enumerations and scoped enumerations. Described in more details in 7.2 Enumeration declarations [dcl.enum].
The values of an enumeration type are not be confused with its enumerators. In your case, since the enumerations you declared all have int
as their underlying types their range of values is the same as that of int
: from INT_MIN
to INT_MAX
(inclusive).
Since 42
has type int
and is obviously a value of int
the behaviour is defined.
Best Answer
Yes, you can convert both ways: int to enum class and enum class to int. This example should be self explanatory:
You can try it yourself here.
[EDIT] Since C++23, we'll have available
std::to_underlying
(at<utility>
), what will allow us to write: