#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
は厳密に型指定された列挙型が達成しようとしているものですが、わずかな違いがあります。通常の列挙型は整数型に変換できますが、厳密に型指定された列挙型はキャストなしではできません。
では、厳密に型指定された列挙値をキャストなしで整数型に変換する方法はありますか?はいの場合、どのように?
質問で言及した範囲の問題だけでなく、複数の問題を解決することを目的とした厳密に型指定された列挙型:
したがって、厳密に型指定された列挙型を整数に、またはその基礎となる型に暗黙的に変換することは不可能です-それがアイデアです。したがって、変換を明示的にするにはstatic_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;
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
キーワードを追加したのは、例外をスローしないためです。
更新
これは、Effective Modern C++ by Scott Meyersにも表示されます。項目10を参照してください(本のコピー内の項目の最終ページで詳しく説明されています)。
#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;
}
いいえ。自然な方法なしがあります。
実際、C++ 11でenum class
を厳密に型指定した背後にある動機の1つは、int
へのサイレント変換を防ぐことです。
これがあなたや誰かに役立つことを願っています
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;
}
暗黙的な変換が(設計上)存在しない理由は、他の回答で説明されています。
私は個人的に単項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); }
上記の投稿が指摘しているように、簡単な答えはできません。しかし、私の場合、私は単に名前空間を乱雑にしたくはなかったが、それでも暗黙の変換を持っているので、私はただやった:
#include <iostream>
using namespace std;
namespace Foo {
enum { bar, baz };
}
int main() {
cout << Foo::bar << endl; // 0
cout << Foo::baz << endl; // 1
return 0;
}
名前空間の並べ替えにより、型の安全性の層が追加されますが、列挙型の値を基になる型に静的にキャストする必要はありません。
これはネイティブの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
の関数でb::B1
を直接返すことができます。それでswitch case
を行うことができます。
また、この例の精神では、テンプレートを(おそらく他のものと一緒に)使用して、enum class
構文で定義された可能性のあるオブジェクトを一般化およびモックできます。
多くの人が言ったように、オーバーヘッドと複雑さを増すことなく自動的に変換する方法はありませんが、シナリオでキャストが少し使用される場合、ラムダを使用することで入力を少し減らして見栄えを良くすることができます。これにより、関数のオーバーヘッド呼び出しが少し追加されますが、以下に示すように、長い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;
}
}