C++ 如何自动将强类型枚举转换为 int?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8357240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:25:44  来源:igfitidea点击:

How to automatically convert strongly typed enum into int?

c++c++11strongly-typed-enum

提问by B?ови?

#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;
}

The a::LOCAL_Ais what the strongly typed enum is trying to achieve, but there is a small difference : normal enums can be converted into integer type, while strongly typed enums can not do it without a cast.

a::LOCAL_A就是强类型枚举试图实现的目标,但有一个小区别:普通枚举可以转换为整数类型,而强类型枚举不能在没有强制转换的情况下转换。

So, is there a way to convert a strongly typed enum value into an integer type without a cast? If yes, how?

那么,有没有办法将强类型枚举值转换为整数类型而不进行强制转换?如果是,如何?

采纳答案by R. Martinho Fernandes

Strongly typed enums aiming to solve multiple problems and not only scoping problem as you mentioned in your question:

强类型枚举旨在解决多个问题,而不仅仅是您在问题中提到的范围问题:

  1. Provide type safety, thus eliminating implicit conversion to integer by integral promotion.
  2. Specify underlying types.
  3. Provide strong scoping.
  1. 提供类型安全,从而消除通过整数提升隐式转换为整数。
  2. 指定基础类型。
  3. 提供强大的范围。

Thus, it is impossible to implicitly convert a strongly typed enum to integers, or even its underlying type - that's the idea. So you have to use static_castto make conversion explicit.

因此,不可能将强类型枚举隐式转换为整数,甚至是其基础类型——这就是想法。所以你必须使用static_cast使转换显式。

If your only problem is scoping and you really want to have implicit promotion to integers, then you better off using not strongly typed enum with the scope of the structure it is declared in.

如果您唯一的问题是范围界定,并且您确实希望隐式提升为整数,那么您最好在声明它的结构的范围内使用非强类型枚举。

回答by R. Martinho Fernandes

As others have said, you can't have an implicit conversion, and that's by-design.

正如其他人所说,您不能进行隐式转换,这是设计使然。

If you want you can avoid the need to specify the underlying type in the cast.

如果需要,您可以避免在强制转换中指定底层类型的需要。

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;

回答by Class Skeleton

A C++14 version of the answer provided by R. Martinho Fernandeswould be:

R. Martinho Fernandes提供的答案的 C++14 版本将是:

#include <type_traits>

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

As with the previous answer, this will work with any kind of enum and underlying type. I have added the noexceptkeyword as it will never throw an exception.

与之前的答案一样,这适用于任何类型的枚举和基础类型。我添加了noexcept关键字,因为它永远不会抛出异常。



Update
This also appears in Effective Modern C++ by Scott Meyers. See item 10 (it is detailed in the final pages of the item within my copy of the book).

更新
这也出现在Scott Meyers 的 Effective Modern C++ 中。请参阅第 10 项(在我的这本书副本中该项目的最后几页中有详细说明)。

回答by Khurshid Normuradov

#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;
}

回答by iammilind

No. There is no natural way.

号有没有自然的方式

In fact, one of the motivations behind having strongly typed enum classin C++11 is to prevent their silent conversion to int.

事实上,enum class在 C++11中强类型背后的动机之一是防止它们无声地转换为int.

回答by Pixelchemist

The reason for the absence of implicit conversion (by design) was given in other answers.

其他答案中给出了没有隐式转换(按设计)的原因。

I personally use unary operator+for the conversion from enum classes to their underlying type:

我个人使用一元operator+进行从枚举类到它们的底层类型的转换:

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);
}

Which gives quite little "typing overhead":

这几乎没有“打字开销”:

std::cout << foo(+b::B2) << std::endl;

Where I actually use a macro to create enums and the operator functions in one shot.

我实际上使用宏来一次性创建枚举和运算符函数。

#define UNSIGNED_ENUM_CLASS(name, ...) enum class name : unsigned { __VA_ARGS__ };\
inline constexpr unsigned operator+ (name const val) { return static_cast<unsigned>(val); }

回答by vis.15

Hope this helps you or someone else

希望这可以帮助您或其他人

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;
}

回答by solstice333

Short answer is you can't as above posts point out. But for my case, I simply didn't want to clutter the namespace but still have implicit conversions, so I just did:

简短的回答是你不能像上面的帖子指出的那样。但就我而言,我只是不想弄乱命名空间但仍然有隐式转换,所以我只是做了:

#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;
}

The namespacing sort of adds a layer of type-safety while I don't have to static cast any enum values to the underlying type.

命名空间类型增加了一层类型安全,而我不必将任何枚举值静态转换为基础类型。

回答by Colliot

This seems impossible with the native enum class, but probably you can mock a enum classwith a class:

这对于 native 来说似乎是不可能的enum class,但也许你可以enum class用 a来模拟a class

In this case,

在这种情况下,

enum class b
{
    B1,
    B2
};

would be equivalent to:

将相当于:

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;
  }
};

This is mostly equivalent to the original enum class. You can directly return b::B1for in a function with return type b. You can do switch casewith it, etc.

这主要等同于原始enum class. 您可以b::B1在返回类型为的函数中直接返回b。你可以switch case用它做,等等。

And in the spirit of this example you can use templates (possibly together with other things) to generalize and mock any possible object defined by the enum classsyntax.

并且本着本示例的精神,您可以使用模板(可能与其他事物一起使用)来概括和模拟由enum class语法定义的任何可能的对象。

回答by Atul Kumar

As many said, there is no way to automatically convert without adding overheads and too much complexity, but you can reduce your typing a bit and make it look better by using lambdas if some cast will be used a bit much in a scenario. That would add a bit of function overhead call, but will make code more readable compared to long static_cast strings as can be seen below. This may not be useful project wide, but only class wide.

正如许多人所说,没有办法在不增加开销和太多复杂性的情况下自动转换,但是如果在某个场景中使用了一些强制转换,您可以通过使用 lambdas 稍微减少输入并使其看起来更好。这会增加一些函数开销调用,但与长的 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;
    }
}