Как автоматически преобразовать строго типизированный enum в int?
#include <iostream>
struct a {
enum LOCAL_A { A1, A2 };
};
enum class b { B1, B2 };
int foo(int input) { return input; }
int main(void) {
std::cout << foo(a::A1) << std::endl;
std::cout << foo(static_cast<int>(b::B2)) << std::endl;
}
A a::LOCAL_A
- это то, что пытается достичь строго типизированное перечисление, но есть небольшая разница: обычные перечисления могут быть преобразованы в целочисленный тип, в то время как строго типизированные перечисления не могут сделать это без преобразования.
Итак, есть ли способ преобразовать строго типизированное значение перечисления в целочисленный тип без приведения? Если да, то как?
Ответы
Ответ 1
Строго типизированные перечисления, нацеленные на решение множества проблем, а не только задачи определения объема, как вы упоминали в своем вопросе:
- Обеспечьте безопасность типов, таким образом устраняя неявное преобразование в целое число путем интегрального продвижения.
- Укажите базовые типы.
- Обеспечить сильное определение объема.
Таким образом, невозможно неявно преобразовать строго типизированное перечисление в целые числа или даже в их базовый тип - в эту идею. Поэтому вы должны использовать static_cast
чтобы сделать преобразование явным.
Если вашей единственной проблемой является область видимости, и вы действительно хотите неявное продвижение к целым числам, то лучше использовать не строго типизированный enum с областью структуры, в которой он объявлен.
Ответ 2
Как говорили другие, вы не можете иметь неявное преобразование и что по-дизайн.
Если вы хотите, вы можете избежать необходимости указывать базовый тип в трансляции.
template <typename E>
constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
return static_cast<typename std::underlying_type<E>::type>(e);
}
std::cout << foo(to_underlying(b::B2)) << std::endl;
Ответ 3
Версия ответа C++ 14, представленная Р. Мартиньо Фернандесом, будет:
#include <type_traits>
template <typename E>
constexpr auto to_underlying(E e) noexcept
{
return static_cast<std::underlying_type_t<E>>(e);
}
Как и в предыдущем ответе, это будет работать с любым типом перечисления и базовым типом. Я добавил ключевое слово noexcept
как оно никогда не выдаст исключение.
Обновить
Это также появляется в Эффективном Современном C++ Скоттом Мейерсом. См. Пункт 10 (он подробно описан на последних страницах этого пункта в моей копии книги).
Ответ 4
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <type_traits>
namespace utils
{
namespace details
{
template< typename E >
using enable_enum_t = typename std::enable_if< std::is_enum<E>::value,
typename std::underlying_type<E>::type
>::type;
} // namespace details
template< typename E >
constexpr inline details::enable_enum_t<E> underlying_value( E e )noexcept
{
return static_cast< typename std::underlying_type<E>::type >( e );
}
template< typename E , typename T>
constexpr inline typename std::enable_if< std::is_enum<E>::value &&
std::is_integral<T>::value, E
>::type
to_enum( T value ) noexcept
{
return static_cast<E>( value );
}
} // namespace utils
int main()
{
enum class E{ a = 1, b = 3, c = 5 };
constexpr auto a = utils::underlying_value(E::a);
constexpr E b = utils::to_enum<E>(5);
constexpr auto bv = utils::underlying_value(b);
printf("a = %d, b = %d", a,bv);
return 0;
}
Ответ 5
Нет. нет естественного пути.
На самом деле, одна из мотиваций, связанных с тем, что в С++ 11 имеет строго типизированный enum class
, заключается в том, чтобы предотвратить их бесшумное преобразование в int
.
Ответ 6
Надеюсь, это поможет вам или кому-то еще.
enum class EnumClass : int //set size for enum
{
Zero, One, Two, Three, Four
};
union Union //This will allow us to convert
{
EnumClass ec;
int i;
};
int main()
{
using namespace std;
//convert from strongly typed enum to int
Union un2;
un2.ec = EnumClass::Three;
cout << "un2.i = " << un2.i << endl;
//convert from int to strongly typed enum
Union un;
un.i = 0;
if(un.ec == EnumClass::Zero) cout << "True" << endl;
return 0;
}
Ответ 7
Причина отсутствия неявного преобразования (по дизайну) была дана в других ответах.
Я лично использую унарный operator+
для преобразования классов enum в их базовый тип:
template <typename T>
constexpr auto operator+(T e) noexcept
-> std::enable_if_t<std::is_enum<T>::value, std::underlying_type_t<T>>
{
return static_cast<std::underlying_type_t<T>>(e);
}
Что дает довольно мало "накладных расходов":
std::cout << foo(+b::B2) << std::endl;
Где я на самом деле использую макрос для создания перечислений и функций оператора одним выстрелом.
#define UNSIGNED_ENUM_CLASS(name, ...) enum class name : unsigned { __VA_ARGS__ };\
inline constexpr unsigned operator+ (name const val) { return static_cast<unsigned>(val); }
Ответ 8
Короткий ответ: вы не можете, как указано выше. Но для моего случая я просто не хотел загромождать пространство имен, но все еще имел неявные преобразования, поэтому я просто сделал:
#include <iostream>
using namespace std;
namespace Foo {
enum Foo { bar, baz };
}
int main() {
cout << Foo::bar << endl; // 0
cout << Foo::baz << endl; // 1
return 0;
}
Пространство имен добавляет слой безопасности типов, в то время как мне не нужно статически приводить значения перечисления к базовому типу.
Ответ 9
Это кажется невозможным с нативным enum class
, но, вероятно, вы можете издеваться над enum class
с помощью class
:
В этом случае
enum class b
{
B1,
B2
};
будет эквивалентно:
class b {
private:
int underlying;
public:
static constexpr int B1 = 0;
static constexpr int B2 = 1;
b(int v) : underlying(v) {}
operator int() {
return underlying;
}
};
Это в основном эквивалентно оригиналу enum class
. Вы можете напрямую возвращать b::B1
для функции с возвращаемым типом b
. Вы можете сделать с ним switch case
и т.д.
И в духе этого примера вы можете использовать шаблоны (возможно, вместе с другими вещами) для обобщения и макета любого возможного объекта, определенного синтаксисом enum class
.
Ответ 10
Как многие говорили, нет никакого способа автоматически конвертировать без добавления накладных расходов и слишком много сложностей, но вы можете немного уменьшить свой набор и сделать его лучше, используя lambdas, если какой-то приведение будет немного использовано в сценарии, Это добавит немного служебных вызовов функции, но сделает код более читабельным по сравнению с длинными статическими статическими строками, как показано ниже. Это может быть не полезно для всего проекта, но только для класса.
#include <bitset>
#include <vector>
enum class Flags { ......, Total };
std::bitset<static_cast<unsigned int>(Total)> MaskVar;
std::vector<Flags> NewFlags;
-----------
auto scui = [](Flags a){return static_cast<unsigned int>(a); };
for (auto const& it : NewFlags)
{
switch (it)
{
case Flags::Horizontal:
MaskVar.set(scui(Flags::Horizontal));
MaskVar.reset(scui(Flags::Vertical)); break;
case Flags::Vertical:
MaskVar.set(scui(Flags::Vertical));
MaskVar.reset(scui(Flags::Horizontal)); break;
case Flags::LongText:
MaskVar.set(scui(Flags::LongText));
MaskVar.reset(scui(Flags::ShorTText)); break;
case Flags::ShorTText:
MaskVar.set(scui(Flags::ShorTText));
MaskVar.reset(scui(Flags::LongText)); break;
case Flags::ShowHeading:
MaskVar.set(scui(Flags::ShowHeading));
MaskVar.reset(scui(Flags::NoShowHeading)); break;
case Flags::NoShowHeading:
MaskVar.set(scui(Flags::NoShowHeading));
MaskVar.reset(scui(Flags::ShowHeading)); break;
default:
break;
}
}