Wie man stark typisierte enum in int automatisch umwandelt?

#include  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(b::B2))<<std::endl; } 

Das a::LOCAL_A ist das, was die stark typisierte a::LOCAL_A erreichen will, aber es gibt einen kleinen Unterschied: normale Enums können in Integer-Typen umgewandelt werden, während stark typisierte Enums ohne Cast nicht möglich sind.

Gibt es eine Möglichkeit, einen stark typisierten Enum-Wert in einen Integer-Typ ohne eine Umwandlung zu konvertieren? Wenn ja, wie?

   

Stark typisierte Enums, die darauf abzielen, mehrere Probleme zu lösen und nicht nur das Scoping-Problem, wie du in deiner Frage erwähnt hast:

  1. Geben Sie type safety ein und eliminieren Sie damit die implizite Konvertierung in Integer durch ganzzahlige Promotion.
  2. Geben Sie die zugrunde liegenden Typen an.
  3. Sorgen Sie für ein starkes Scoping.

Daher ist es unmöglich, eine stark typisierte Enumeration implizit in ganze Zahlen oder sogar in den darunter liegenden Typ zu konvertieren – das ist die Idee. Sie müssen also static_cast , um die Konvertierung explizit zu machen.

Wenn Ihr einziges Problem das Scoping ist und Sie implizit zu Ganzzahlen übergehen wollen, dann sollten Sie nicht stark typisierte enum mit dem Bereich der Struktur verwenden, in der es deklariert ist.

Ich hoffe es hilft!

Wie andere bereits gesagt haben, können Sie keine implizite Konvertierung durchführen, und das ist By-Design.

Wenn Sie möchten, können Sie die Angabe des zugrunde liegenden Typs in der Besetzung vermeiden.

 template  constexpr typename std::underlying_type::type to_underlying(E e) noexcept { return static_cast::type>(e); } std::cout < < foo(to_underlying(b::B2)) << std::endl; 

Eine C ++ 14-Version der Antwort von R. Martinho Fernandes wäre:

 #include  template  constexpr auto to_underlying(E e) noexcept { return static_cast>(e); } 

Wie bei der vorherigen Antwort funktioniert dies mit jeder Art von Enum und dem zugrunde liegenden Typ. Ich habe das noexcept Schlüsselwort hinzugefügt, da es niemals eine Ausnahme noexcept .


Aktualisieren
Dies zeigt sich auch in Effective Modern C ++ von Scott Meyers . Siehe Punkt 10 (es ist auf den letzten Seiten des Artikels in meiner Kopie des Buches detailliert).

 #include  #include  #include  #include  namespace utils { namespace details { template< typename E > using enable_enum_t = typename std::enable_if< std::is_enum::value, typename std::underlying_type::type >::type; } // namespace details template< typename E > constexpr inline details::enable_enum_t underlying_value( E e )noexcept { return static_cast< typename std::underlying_type::type >( e ); } template< typename E , typename T> constexpr inline typename std::enable_if< std::is_enum::value && std::is_integral::value, E >::type to_enum( T value ) noexcept { return static_cast( 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(5); constexpr auto bv = utils::underlying_value(b); printf("a = %d, b = %d", a,bv); return 0; } 

Nein, es gibt keinen natürlichen Weg .

In der Tat ist eine der Motivationen hinter einer stark typisierten enum class in C ++ 11, ihre stille Umwandlung in int zu verhindern.

Hoffe, das hilft dir oder jemand anderem

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

Der Grund für das Fehlen einer impliziten Konvertierung (durch Design) wurde in anderen Antworten gegeben.

Ich persönlich benutze unären operator+ für die Umwandlung von Enum-classn in den zugrunde liegenden Typ:

 template  constexpr auto operator+(T e) noexcept -> std::enable_if_t::value, std::underlying_type_t> { return static_cast>(e); } 

Das gibt ziemlich wenig “Schreibaufwand”:

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

Wo ich tatsächlich ein Makro verwende, um Enums zu erstellen und der Operator funktioniert in einem Schuss.

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

Kurze Antwort ist, dass Sie nicht wie oben erwähnt werden können. Aber für meinen Fall wollte ich einfach nicht den Namespace durcheinander bringen, aber immer noch implizite Conversions, also habe ich einfach:

 #include  using namespace std; namespace Foo { enum { bar, baz }; } int main() { cout < < Foo::bar << endl; // 0 cout << Foo::baz << endl; // 1 return 0; } 

Die Namespacing-Art fügt eine Schicht von Typ-Sicherheit hinzu, während ich keine Enum-Werte für den zugrunde liegenden Typ statisch darstellen muss.

Dies scheint mit der nativen enum class , aber Sie können eine enum class wahrscheinlich mit einer class verspotten:

In diesem Fall,

 enum class b { B1, B2 }; 

wäre äquivalent zu:

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

Dies entspricht weitgehend der ursprünglichen enum class . Sie können b::B1 für eine function mit Rückgabetyp b direkt zurückgeben. Sie können switch case mit ihm switch case , usw.

Und im Sinne dieses Beispiels können Sie Vorlagen (möglicherweise zusammen mit anderen Dingen) verwenden, um jedes mögliche Objekt, das durch die enum class definiert wird, zu verallgemeinern und zu verspotten.

Wie viele sagen, es gibt keine Möglichkeit, automatisch zu konvertieren, ohne Overheads und zu viel Komplexität hinzuzufügen, aber Sie können Ihre Eingabe ein wenig reduzieren und es besser aussehen lassen, indem Sie lambdas verwenden, wenn einige Darsteller in einem Szenario viel verwendet werden. Das würde einen kleinen Overhead-Aufruf der function hinzufügen, aber den Code im Vergleich zu langen static_cast-Strings lesbarer machen, wie unten zu sehen ist. Dies ist möglicherweise nicht projektweit sinnvoll, sondern nur klassenweit.

 #include  #include  enum class Flags { ......, Total }; std::bitset(Total)> MaskVar; std::vector NewFlags; ----------- auto scui = [](Flags a){return static_cast(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; } }