[英]How to automatically convert strongly typed enum into 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::LOCAL_A
是強類型枚舉試圖實現的,但有一個小的區別:普通枚舉可以轉換為整數類型,而強類型枚舉在沒有強制轉換的情況下無法做到。
那么,有沒有一種方法可以將強類型枚舉值轉換為整數類型而無需強制轉換? 如果是,如何?
正如其他人所說,您不能進行隱式轉換,這是設計使然。
如果您願意,您可以避免在強制轉換中指定基礎類型。
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;
強類型枚舉旨在解決多個問題,而不僅僅是您在問題中提到的范圍問題:
因此,不可能將強類型枚舉隱式轉換為整數,甚至是其底層類型——這就是想法。 所以你必須使用static_cast
來明確轉換。
如果您唯一的問題是作用域,並且您確實希望對整數進行隱式提升,那么您最好使用具有聲明它的結構范圍的非強類型枚舉。
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 項(在我的書副本中該項目的最后幾頁中有詳細說明)。
C++23 版本將使用std::to_underlying函數:
#include <utility>
std::cout << std::to_underlying(b::B2) << std::endl;
...或者如果基礎類型可能是1 字節類型:
std::cout << +(std::to_underlying(b::B2)) << std::endl;
在其他答案中給出了沒有隱式轉換(通過設計)的原因。
我個人使用一元運算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); }
簡短的回答是你不能像上面的帖子指出的那樣。 但就我而言,我只是不想弄亂命名空間但仍然有隱式轉換,所以我只是這樣做了:
#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;
}
命名空間增加了一層類型安全,而我不必將任何枚舉值靜態轉換為基礎類型。
不,沒有自然的方法。
事實上,在 C++11 中使用強類型enum class
的動機之一是防止它們靜默轉換為int
。
#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;
}
希望這對您或其他人有所幫助
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;
}
這對於原生的enum class
來說似乎是不可能的,但也許你可以用一個class
來模擬一個enum 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
的函數中直接返回b::B1
for 。 你可以用它做switch case
,等等。
並且本着本示例的精神,您可以使用模板(可能與其他東西一起)來概括和模擬enum class
語法定義的任何可能的對象。
正如許多人所說,沒有辦法在不增加開銷和太多復雜性的情況下自動轉換,但是如果在某個場景中會使用一些強制轉換,您可以使用 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;
}
}
C++ 委員會向前邁了一步(將枚舉范圍限定在全局命名空間之外),后退了 50 步(沒有枚舉類型衰減為整數)。 遺憾的是,如果您需要以任何非符號方式獲取枚舉的值,那么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
}
問題:
有沒有辦法將強類型枚舉值轉換為整數類型而無需強制轉換? 如果是,如何?
回答:
不,那里沒有。 強類型枚舉不能在沒有顯式轉換的情況下轉換為整數。 然而,弱枚舉可以,因為它們會被自動隱式轉換。 因此,如果您想自動隱式轉換為 int,請考慮改用 C 風格的弱枚舉(請參閱下面的“進一步介紹”部分中的更多內容)。
從這里(強調添加): https://en.cppreference.com/w/cpp/language/enum --> 在“Scoped enumerations”部分下:
沒有從作用域枚舉器 [AKA: "strong enum"] 的值到整數類型的隱式轉換,盡管可以使用
static_cast
來獲取枚舉器的數值。
enum class
)枚舉類型在 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 explicit casting works just fine!:
int i1 = static_cast<int>(e); // explicit C++-style cast
int i2 = (int)e; // explicit C-style cast
第一個“功能”實際上可能是您不想要的東西,在這種情況下,您只需要使用常規的 C 樣式枚舉來代替! 好消息是:您仍然可以像在 C 中所做的那樣,通過簡單地在枚舉類型名稱前面加上枚舉類型名稱,來為枚舉“范圍”或“命名空間”,如下所示:
// regular enum (AKA: "weak" or "C-style" enum)
enum my_enum
{
// C-style-scoped through the `MY_ENUM_` prefix
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/BkWGqlqz_。
主.cpp :
#include <iostream>
#include <stdio.h>
// enum class (AKA: "strong" or "scoped" enum [available only in C++, not C])
enum class my_enum
{
A = 0,
B,
C,
};
// regular enum (AKA: "weak" or "C-style" enum [available in BOTH C and C++])
enum my_enum2
{
MY_ENUM_A = 0,
MY_ENUM_B,
MY_ENUM_C,
};
int main()
{
printf("Hello World\n");
// 1) scoped enum
my_enum e = my_enum::A; // scoped through `my_enum::`
e = my_enum::B;
// IMPLICIT CASTING TO INT IS NOT ALLOWED!
// int i = e; // "error: cannot convert ‘my_enum’ to ‘int’ in initialization"
// But this explicit C++-style cast works fine:
int i1 = static_cast<int>(e);
// This explicit C-style cast works fine too, and is easier to read
int i2 = (int)e;
// 2) regular enum
my_enum2 e2 = MY_ENUM_A; // scoped through `MY_ENUM_`
e2 = MY_ENUM_B;
// This implicit cast works fine / IS allowed on C-style enums!
int i3 = e2;
// These explicit casts are also fine, but explicit casting is NOT
// required for regular enums.
int i4 = static_cast<int>(e2); // explicit C++-style cast
int i5 = (int)e2; // explicit C-style cast
return 0;
}
enum class
枚舉的完整示例:如何迭代枚舉? 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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.