enum class A : int { zero, one };
void foo(int) {}
int main() { foo(A::zero); }enum class table1_cols { id, name };
enum class table2_cols { id, address };template<class integral_t>
operator integral_t(table1_cols t)
{
static_assert(std::is_integral<integral_t>(), "Only to integrals!!");
return static_cast<int>(t);
}
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/b9b24701-fd4e-411b-91e1-608bba06fe96%40isocpp.org.
Enum classes doesn't allow, by default, implicit conversion, not even to the underlying type:enum class A : int { zero, one };
void foo(int) {}
int main() { foo(A::zero); }and that is a good thing, but sometimes, you need a enum class only to avoid name colissions, for example:enum class table1_cols { id, name };
enum class table2_cols { id, address };If table1_cols and table2_cols were raw enums, there would be a colission between both id named values, since they have global namespace scope,
I posted here previously about adding a 'std::underlying_cast' for enums.People agreed with the concept, but not the name (which is fine).
enum class blah {...};
auto operator+(blah b) {return static_cast<std::underlying_type_t<blah>>(b);}Now, you could add new overloads for every enum you have, but hopefully we can all agree that that would be daft.
The language needs to make this easy
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/bb14bcfe-f2fb-453e-834a-12e973d7ec57%40isocpp.org.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/bb14bcfe-f2fb-453e-834a-12e973d7ec57%40isocpp.org.
> The conversion from enum to integer is not supposed to be a common operationAccording to who? Enums aren't supposed to be printed or serialised? That's news to me.Why doesn't this code 'just work' when T is an enum?
> Not only that, the OP suggested being able to add something to an enumeration definition to make it implicitly convertibleWhat else needs to be added?
enum class E : bool { ... }The compiler already knows everything it needs, but chooses (at the moment) to pretend it doesn't.
> This is a tool for the rarer enumerations where this kind of conversion is frequent. Type-safety should not be so lightly discarded.'Rarer' is entirely subjective. Totally agree on the type safety and so nothing should be implicitly converted.std::underlying_cast (don't worry about the actual name), if adopted, makes it pretty clear to developers what's going on, far more so than an operator would.
auto lamb = [](...) {...};
auto func_ptr = -lamb;Then, the above code could be written as:std::ostream& operator<<(std::ostream& os, const T& value){os << std::underlying_cast(value);return os;}This, in my opinion, would count as the language making something easy, as much as that goes against the ethos of C++.
template<typename T> requires is_enum_v<T>
auto underlying_cast(T t) {return static_cast<std::underlying_type_t<T>>(t);}`+` is one character. I feel that's a reasonable compromise between breaking type safety (by declaring the type as a whole to be implicitly convertible) and the verbosity of invoking that conversion.
namespace col_enum {
template<class enum_t>
inline auto operator+(enum_t e) noexcept
{
static_assert(std::is_enum_v<enum_t>, "Only for enums!!");
return std::underlying_type<enum_t>(e);
}
template<class enum_t>
inline std::string title(enum_t e); // Header title / string version of the enum
template<class enum_t>
inline std::enable_if_t<std::is_enum_v<enum_t>::value, std::ostream&>
operator<<(std::ostream& os, enum_t e)
{ return os << title(e); }
template<class enum_t>
inline auto& operator++(enum_t& e) noexcept
{ return e = enum_t(+e + 1); }
// "Table size"
template<class enum_t>
constexpr std::size_t size() noexcept;
// We assume one is an enumerator and the other
// is an integral type, or two enumerators of same type.
// For simplicity, no further check is done (it should though).
template<class a_t, class b_t>
inline bool operator==(a_t const& a, b_t const& b)
{
using cmp_t = std::common_type_t<decltype(+a), decltype(+b)>;
return static_cast<cmp_t>(a) == static_cast<cmp_t>(b);
}
template<class a_t, class b_t>
inline bool operator!=(a_t a, b_t b) { return !(a == b); }
// Any other "enumerator-member" or operator, if needed.
}
// User-code
namespace col_enum {
enum class table1_col { id, name };
template<>
constexpr std::size_t size<table1_col>() noexcept { return 2; }
template<>
inline std::string title<table1_col>(table1_col c)
{
switch(c) {
case table1_col::id:
return "ID";
case table1_col::name:
return "name";
default:
throw std::logic_error("invalid col");
}
}
}
namespace col_enum {
// Other enumerator for other table,
// with their corresponding methods.
}
class my_table1_model
{
public:
using col_t = col_enum::table1_col;
// ...
void fun(int c)
{
if (c >= col_enum::size<col_t>())
throw std::logic_error("Whaaat?");
if (c == col_t::id)
third_party_fun(this, +col_t::name,
boost::lexical_cast<std::string>(col_t::name));
}
};Granted, `+` may not be the best unary operator to use here, but feel free to use whatever seems natural.