#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
是强类型枚举试图实现的,但是有一点不同:普通枚举可以转换为整数类型,而强类型枚举不能在没有强制转换的情况下执行。
那么,有没有办法将强类型的枚举值转换为没有强制转换的整数类型?如果是,怎么样?
答案 0 :(得分:130)
正如其他人所说的那样,你不能进行隐式转换,这是按设计进行的。
如果您愿意,可以避免在演员表中指定基础类型。
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;
答案 1 :(得分:109)
强烈打印的枚举旨在解决多个问题,而不仅仅是您在问题中提到的范围问题:
因此,不可能将强类型枚举隐式转换为整数,甚至是它的基础类型 - 这就是想法。因此,您必须使用static_cast
来明确转换。
如果您唯一的问题是作用域并且您确实希望对整数进行隐式提升,那么最好不要使用非强类型枚举来声明它所声明的结构的范围。
答案 2 :(得分:55)
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);
}
与前面的答案一样,这适用于任何类型的枚举和基础类型。我添加了noexcept
关键字,因为它永远不会抛出异常。
更新
这也出现在Scott Meyers的 Effective Modern C ++ 中。见第10项(在我的书副本中的项目的最后几页详细说明。)
答案 3 :(得分:17)
#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;
}
答案 4 :(得分:15)
没有。 没有自然的方式。
事实上,在C ++ 11中强烈键入enum class
背后的动机之一是阻止他们将{s}转换为int
。
答案 5 :(得分:12)
希望这可以帮助你或其他人
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;
}
答案 6 :(得分:10)
在其他答案中给出了缺乏隐式转换(按设计)的原因。
我个人使用一元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);
}
这给了很少的“打字开销”:
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); }
答案 7 :(得分:9)
简短的回答是你不能像上面的帖子所指出的那样。但对于我的情况,我只是不想弄乱命名空间但仍然有隐式转换,所以我只是做了:
#include <iostream>
using namespace std;
namespace Foo {
enum { bar, baz };
}
int main() {
cout << Foo::bar << endl; // 0
cout << Foo::baz << endl; // 1
return 0;
}
命名空间类型添加了一个类型安全层,而我不必将任何枚举值静态转换为基础类型。
答案 8 :(得分:5)
使用原始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
语法定义的任何可能的对象。
答案 9 :(得分:4)
正如许多人所说的那样,没有办法在不增加开销和太多复杂性的情况下自动转换,但是你可以通过使用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;
}
}
答案 10 :(得分:3)
C ++委员会向前迈进了一步(将枚举范围从全局名称空间中确定),向后退了五十步(没有枚举类型衰减为整数)。可悲的是,如果您需要以任何非符号方式获取枚举的值,enum class
就根本无法使用。
最好的解决方案是根本不使用它,而是使用命名空间或结构自己定义枚举的范围。为此,它们是可互换的。在引用枚举类型本身时,您将需要输入一些额外的信息,但这可能并不常见。
struct TextureUploadFormat {
enum Type : uint32 {
r,
rg,
rgb,
rgba,
__count
};
};
// must use ::Type, which is the extra typing with this method; beats all the static_cast<>()
uint32 getFormatStride(TextureUploadFormat::Type format){
const uint32 formatStride[TextureUploadFormat::__count] = {
1,
2,
3,
4
};
return formatStride[format]; // decays without complaint
}
答案 11 :(得分:2)
对来自 R. Martinho Fernandes 和 Class Skeleton 的答案的扩展:他们的答案展示了如何使用 typename std::underlying_type<EnumType>::type
或 std::underlying_type_t<EnumType>
将您的枚举值转换为 static_cast
到基础类型的值。与对某些特定整数类型的 static_cast
相比,例如 static_cast<int>
,这具有维护友好的好处,因为当底层类型发生变化时,使用 std::underlying_type_t
的代码将自动使用新的类型。
然而,这有时不是您想要的:假设您想直接打印枚举值,例如打印到 std::cout
,如下例所示:
enum class EnumType : int { Green, Blue, Yellow };
std::cout << static_cast<std::underlying_type_t<EnumType>>(EnumType::Green);
如果您稍后将基础类型更改为字符类型,例如 uint8_t
,那么 EnumType::Green
的值将不会打印为数字,而是打印为字符,这很可能不是你想要什么。因此,您有时更愿意将枚举值转换为“底层类型,但在必要时进行整数提升”。
如有必要,可以将一元 operator+
应用于强制转换的结果以强制整数提升。但是,您也可以使用 std::common_type_t
(也来自头文件 <type_traits>
)来执行以下操作:
enum class EnumType : int { Green, Blue, Yellow };
std::cout << static_cast<std::common_type_t<int, std::underlying_type_t<EnumType>>>(EnumType::Green);
最好将此表达式包装在一些辅助模板函数中:
template <class E>
constexpr std::common_type_t<int, std::underlying_type_t<E>>
enumToInteger(E e) {
return static_cast<std::common_type_t<int, std::underlying_type_t<E>>>(e);
}
这样对眼睛更友好,对底层类型的更改更友好,并且不需要使用 operator+
的技巧:
std::cout << enumToInteger(EnumType::Green);
答案 12 :(得分:0)
从{strong>范围枚举器 [AKA:“ strong enum”]的值到整数类型没有隐式转换,尽管
static_cast
可用于获取变量的数值。枚举器。
(添加了重点)
来源:https://en.cppreference.com/w/cpp/language/enum->在“范围枚举”部分下。
在C ++中,有两种类型的枚举:
“作用域”枚举或“强”枚举除了“常规”枚举提供的功能外,还提供了另外两个“功能”。范围枚举:
枚举类的示例:
// enum class (AKA: "strong" or "scoped" enum)
enum class my_enum
{
A = 0,
B,
C,
};
my_enum e = my_enum::A; // scoped through `my_enum::`
e = my_enum::B;
// NOT ALLOWED!:
// error: cannot convert ‘my_enum’ to ‘int’ in initialization
// int i = e;
// But this works fine:
int i = static_cast<int>(e);
第一个“功能”实际上可能是您不想要的,在这种情况下,您只需要使用常规的C样式枚举即可!妙处是:您仍然可以像用C语言几十年来那样“枚举”或“命名空间”枚举,只需在其名称前面加上枚举类型名称即可,如下所示:
常规枚举的示例:
// regular enum (AKA: "weak" or "C-style" enum)
enum my_enum
{
MY_ENUM_A = 0,
MY_ENUM_B,
MY_ENUM_C,
};
my_enum e = MY_ENUM_A; // scoped through `MY_ENUM_`
e = MY_ENUM_B;
// This works fine!
int i = e;
请注意,只需将MY_ENUM_
“作用域”添加到每个枚举的前面,您仍然可以获得“作用域”的好处!
在此处测试上面的代码:https://onlinegdb.com/SJQ7uthcP。