Как автоматически конвертировать строго типизированный enum в int? - PullRequest
135 голосов
/ 02 декабря 2011
#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::LOCAL_A - это то, что пытается достичь строго типизированное перечисление, но есть небольшая разница: обычные перечисления могут быть преобразованы в целочисленный тип, в то время как строго типизированные перечисления не могут сделать это без преобразования.

Итак, есть ли способ преобразовать строго типизированное значение перечисления в целочисленный тип без приведения?Если да, то как?

Ответы [ 10 ]

133 голосов
/ 02 декабря 2011

Как уже говорили другие, у вас не может быть неявного преобразования, и это не совсем понятно.

Если вы хотите, вы можете избежать необходимости указывать базовый тип в приведении.

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;
111 голосов
/ 02 декабря 2011

Строго типизированные перечисления, нацеленные на решение нескольких задач, а не только задачи определения объема, как вы упомянули в своем вопросе:

  1. Обеспечьте безопасность типов, таким образом устраняя неявное преобразование в целое число путем интегрального продвижения.
  2. Укажите базовые типы.
  3. Обеспечьте строгую область видимости.

Таким образом, невозможно неявно преобразовать строго типизированное перечисление в целые числа или даже в его базовый тип - такова идея.Поэтому вы должны использовать static_cast, чтобы сделать преобразование явным.

Если ваша единственная проблема - это область видимости, и вы действительно хотите иметь неявное повышение до целых чисел, то лучше использовать не строго типизированный enum с областью действияструктура объявлена ​​в.

59 голосов
/ 12 октября 2015

Версия ответа на C ++ 14, предоставленная R. Мартиньо Фернандес будет:

#include <type_traits>

template <typename E>
constexpr auto to_underlying(E e) noexcept
{
    return static_cast<std::underlying_type_t<E>>(e);
}

Как и в предыдущем ответе, это будет работать с любым типом перечисления и базовым типом. Я добавил ключевое слово noexcept, так как оно никогда не выдаст исключение.


Обновление
Это также появляется в Effective Modern C ++ Скотта Мейерса . См. Пункт 10 (он подробно описан на последних страницах этого пункта в моей копии книги).

18 голосов
/ 07 сентября 2013
#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;
}
15 голосов
/ 02 декабря 2011

Нет. нет естественного пути .

На самом деле, одна из причин, по которым строго набирается enum class в C ++ 11, состоит в том, чтобы предотвратить их тихое преобразование в int. * 1007.*

13 голосов
/ 10 октября 2013

Надеюсь, это поможет вам или кому-то еще

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;
}
11 голосов
/ 13 февраля 2017

Причина отсутствия неявного преобразования (по замыслу) была дана в других ответах.

Я лично использую унарный 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); }
9 голосов
/ 19 сентября 2017

Короткий ответ: вы не можете, как указано выше.Но в моем случае я просто не хотел загромождать пространство имен, но все еще имел неявные преобразования, поэтому я просто сделал:необходимо выполнить статическое приведение любых значений перечисления к базовому типу.

5 голосов
/ 06 июня 2017

Это кажется невозможным для нативного 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.

4 голосов
/ 22 марта 2015

Как уже говорили многие, нет способа автоматически конвертировать без добавления накладных расходов и слишком большой сложности, но вы можете немного уменьшить объем печати и улучшить ее, используя лямбда-выражения, если в сценарии будет использовано немного приведения.,Это добавит немного служебных вызовов, но сделает код более читабельным по сравнению с длинными строками static_cast, как можно увидеть ниже.Это может быть не полезно для всего проекта, но только для всего класса.

#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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...