įtraukimas į eilutę šiuolaikinėje C ++ 11 / C ++ 14 / C ++ 17 ir ateityje C ++ 20

Priešingai nei visi kiti panašūs klausimai, šis klausimas susijęs su naujų C ++ funkcijų naudojimu.

Perskaitęs daug atsakymų, niekada neradau:

pavyzdys

Pavyzdys dažnai yra geresnis nei ilgas paaiškinimas.
Šį fragmentą galite sukompiliuoti ir paleisti „ Coliru“ .
( Taip pat galimas ir kitas ankstesnis pavyzdys )

 #include <map> #include <iostream> struct MyClass { enum class MyEnum : char { AAA = -8, BBB = '8', CCC = AAA + BBB }; }; // Replace magic() by some faster compile-time generated code // (you're allowed to replace the return type with std::string // if that easier for you) const char* magic (MyClass::MyEnum e) { const std::map<MyClass::MyEnum,const char*> MyEnumStrings { { MyClass::MyEnum::AAA, "MyClass::MyEnum::AAA" }, { MyClass::MyEnum::BBB, "MyClass::MyEnum::BBB" }, { MyClass::MyEnum::CCC, "MyClass::MyEnum::CCC" } }; auto it = MyEnumStrings.find(e); return it == MyEnumStrings.end() ? "Out of range" : it->second; } int main() { std::cout << magic(MyClass::MyEnum::AAA) <<'\n'; std::cout << magic(MyClass::MyEnum::BBB) <<'\n'; std::cout << magic(MyClass::MyEnum::CCC) <<'\n'; } 

Apribojimai

  • Prašome ne neįkainojamą kitų atsakymų ar pirminių nuorodų dubliavimą.
  • Prašome vengti patobulintų makroekonominių atsakymų arba stengtis sumažinti #define išlaidas kiek įmanoma mažiau.
  • Nenaudokite enumstring maping.

Būtų malonu turėti

  • Išlaikyti enum reikšmes, pradedant nuo kito skaičiaus nei nulis
  • Išlaikyti neigiamas enum reikšmes
  • Parama suskaidytoms enum vertėms
  • Enum class enum palaikymas „ class enum (C ++ 11)
  • Enumeration class enum: <type> support class enum: <type> class enum: <type> class enum: <type> turi bet kokį galiojantį <type> (C ++ 11)
  • Konversijos kompiliavimo metu (ne vykdymo metu) iki eilutės,
    arba bent jau greitas vykdymas vykdymo metu (pvz., std::map nėra gera idėja ...)
  • constexpr (C ++ 11, atsipalaidavęs C ++ 14)
  • noexcept (C ++ 11)
  • Fragmentas C ++ 14 / C ++ 17 draugiškas
  • C ++ technologijos būsena

Viena iš galimų idėjų būtų naudoti C + + kompiliatoriaus funkcijas, kad sukurtų C + + kodą, naudojant variadic template class gudrybes, pagrįstas variadic template class ir constexpr .

233
03 марта '15 в 13:05 2015-03-03 13:05 olibre yra nustatytas kovo 03 '15 , 13:05 2015-03-03 13:05
@ 26 atsakymai

Atrodo, jog Jurijus Finkelšteinas; bet reklamos nereikia. Aš naudoju žemėlapį taip, kad bet kokias eilutes galite priskirti bet kokiai reikšmei.

„Enum“ klasės deklaracija:

 DECLARE_ENUM_WITH_TYPE(TestEnumClass, int32_t, ZERO = 0x00, TWO = 0x02, ONE = 0x01, THREE = 0x03, FOUR); 

Šis kodas automatiškai sukurs enum klasę ir perkrovą:

  • '+' '+ =' std :: string
  • „<<“ siūlams
  • „~“ yra tik konvertuoti į eilutę (bet kuris unarusis operatorius tai padarys, bet aš asmeniškai nemėgstu aiškumo)
  • „*“, kad gautumėte pervedimų skaičių

Nereikalaujama atnaujinti, pateikiamos visos būtinos funkcijos.

Kodas:

 #include <algorithm> #include <iostream> #include <map> #include <sstream> #include <string> #include <vector> #define STRING_REMOVE_CHAR(str, ch) str.erase(std::remove(str.begin(), str.end(), ch), str.end()) std::vector<std::string> splitString(std::string str, char sep = ',') { std::vector<std::string> vecString; std::string item; std::stringstream stringStream(str); while (std::getline(stringStream, item, sep)) { vecString.push_back(item); } return vecString; } #define DECLARE_ENUM_WITH_TYPE(E, T, ...) \ enum class E : T \ { \ __VA_ARGS__ \ }; \ std::map<T, std::string> E##MapName(generateEnumMap<T>(#__VA_ARGS__)); \ std::ostream   E enumTmp) \ { \ os << E##MapName[static_cast<T>(enumTmp)]; \ return os; \ } \ size_t operator*(E enumTmp) { (void) enumTmp; return E##MapName.size(); } \ std::string operator~(E enumTmp) { return E##MapName[static_cast<T>(enumTmp)]; } \ std::string operator+(std::string  E enumTmp) { return str + E##MapName[static_cast<T>(enumTmp)]; } \ std::string operator+(E enumTmp, std::string  { return E##MapName[static_cast<T>(enumTmp)] + str; } \ std::string   E enumTmp) \ { \ str += E##MapName[static_cast<T>(enumTmp)]; \ return str; \ } \ E operator++(E  \ { \ auto iter = E##MapName.find(static_cast<T>(enumTmp)); \ if (iter == E##MapName.end() || std::next(iter) == E##MapName.end()) \ iter = E##MapName.begin(); \ else \ { \ ++iter; \ } \ enumTmp = static_cast<E>(iter->first); \ return enumTmp; \ } \ bool valid##E(T value) { return (E##MapName.find(value) != E##MapName.end()); } #define DECLARE_ENUM(E, ...) DECLARE_ENUM_WITH_TYPE(E, int32_t, __VA_ARGS__) template <typename T> std::map<T, std::string> generateEnumMap(std::string strMap) { STRING_REMOVE_CHAR(strMap, ' '); STRING_REMOVE_CHAR(strMap, '('); std::vector<std::string> enumTokens(splitString(strMap)); std::map<T, std::string> retMap; T inxMap; inxMap = 0; for (auto iter = enumTokens.begin(); iter != enumTokens.end(); ++iter) { // Token: [EnumName | EnumName=EnumValue] std::string enumName; T enumValue; if (iter->find('=') == std::string::npos) { enumName = *iter; } else { std::vector<std::string> enumNameValue(splitString(*iter, '=')); enumName = enumNameValue[0]; //inxMap = static_cast<T>(enumNameValue[1]); if (std::is_unsigned<T>::value) { inxMap = static_cast<T>(std::stoull(enumNameValue[1], 0, 0)); } else { inxMap = static_cast<T>(std::stoll(enumNameValue[1], 0, 0)); } } retMap[inxMap++] = enumName; } return retMap; } 

Pavyzdys:

 DECLARE_ENUM_WITH_TYPE(TestEnumClass, int32_t, ZERO = 0x00, TWO = 0x02, ONE = 0x01, THREE = 0x03, FOUR); int main(void) { TestEnumClass first, second; first = TestEnumClass::FOUR; second = TestEnumClass::TWO; std::cout << first << "(" << static_cast<uint32_t>(first) << ")" << std::endl; // FOUR(4) std::string strOne; strOne = ~first; std::cout << strOne << std::endl; // FOUR std::string strTwo; strTwo = ("Enum-" + second) + (TestEnumClass::THREE + "-test"); std::cout << strTwo << std::endl; // Enum-TWOTHREE-test std::string strThree("TestEnumClass: "); strThree += second; std::cout << strThree << std::endl; // TestEnumClass: TWO std::cout << "Enum count=" << *first << std::endl; } 

Kodą galite paleisti čia

17
16 февр. Danilo Ramos atsakymas, pateiktas vasario 16 d. 2018-02-16 07:25 '18, 07:25 2018-02-16 07:25

( Geresnis_enums bibliotekos metodas )

Dabartiniame „C ++“ yra toks būdas:

 ENUM(Channel, char, Red = 1, Green, Blue) // "Same as": // enum class Channel : char { Red = 1, Green, Blue }; 

Naudoti:

 Channel c = Channel::_from_string("Green"); // Channel::Green (2) c._to_string(); // string "Green" for (Channel c : Channel::_values()) std::cout << c << std::endl; // And so on... 

Visos operacijos gali būti atliekamos constexpr . Taip pat galite įgyvendinti [refleksyviąją C ++ 17 sąlygą, minėtą @ecatmur atsakyme.

  • Yra tik vienas makro. Manau, kad tai yra kuo mažesnis, nes išankstinio apdorojimo eilutė ( # ) yra vienintelis būdas konvertuoti simbolį į eilutę dabartinėje C + +.
  • Makro yra gana nepatrauklus - pastovios deklaracijos, įskaitant iniciatorius, įterpiamos į įmontuotą enum deklaraciją. Tai reiškia, kad jie turi tokią pačią sintaksę ir reikšmę kaip ir integruotas sąrašas.
  • Pasikartojimas pašalinamas.
  • Įgyvendinimas yra natūraliausias ir naudingiausias ne mažiau kaip C ++ 11, dėka constexpr . Jis taip pat gali būti sukonfigūruotas dirbti su C ++ 98 + __VA_ARGS__ . Tai tikrai modernus C ++.

Makro apibrėžimas yra šiek tiek susijęs, todėl į jį atsakau keliais būdais.

  • Dauguma šio atsakymo yra įgyvendinimas, kuris, mano manymu, tinka riboti erdvę „StackOverflow“.
  • Taip pat yra CodeProject straipsnis, kuriame išsamiai aprašoma įgyvendinimo pagrindai. [Ar turėčiau jį perkelti čia? Manau, kad tai per daug atsakymui į atsakymą.
  • Yra pilnai įrengta biblioteka „Better Enums“, kuri įgyvendina makrokomandą viename antraštės faile. Jame taip pat įgyvendinami prašymai dėl N4428 tipo savybių , šiuo metu persvarstytas pasiūlymas dėl C ++ 17 N4113 atspindėjimo. Taigi, bent jau šiai makrokomandai deklaruotoms sumoms, galite pasiūlyti C ++ 17 enum siūlomą atspindį dabar C + + 11 / C ++ 14.

Šio atsakymo išplėtimas į bibliotekos funkcijas nėra sudėtingas - čia nėra nieko „svarbaus“. Tačiau tai yra gana varginantis ir yra kompiliatoriaus perkėlimo problemų.

Atsakomybės apribojimas : esu ir CodeProject straipsnio, ir bibliotekos autorius.

Galite išbandyti kodą iš šio atsakymo , bibliotekos ir N4428 diegimo internetu Wandbox. Bibliotekos dokumentacijoje taip pat apžvelgiama, kaip ją naudoti kaip N4428 , kuri paaiškina šio sakinio dalį.


paaiškinimas

Toliau pateiktas kodas įgyvendina konversijas tarp enumų ir styginių. Tačiau jis gali būti išplėstas ir kitiems dalykams, pvz., Iteracijai. Šis atsakas apgaubia struct . Taip pat galite sukurti struct atributus kartu su skaičiavimu.

Strategija yra sukurti kažką panašaus:

 struct Channel { enum _enum : char { __VA_ARGS__ }; constexpr static const Channel _values[] = { __VA_ARGS__ }; constexpr static const char * const _names[] = { #__VA_ARGS__ }; static const char* _to_string(Channel v) {  } constexpr static Channel _from_string(const char *s) {  } }; 

Problemos:

  1. Kaip rezultatas, mes gauname kažką panašaus {Red = 1, Green, Blue} kaip iniciatorių vertėms. Tai neleidžiama C ++, nes Red nėra priskiriama išraiška. Tai išsprendžiama kiekvienam konstantui į T tipą, kuris turi priskyrimo operatorių, bet atmeta užduotį: {(T)Red = 1, (T)Green, (T)Blue} .
  2. Tokiu pat būdu mes gauname {"Red = 1", "Green", "Blue"} kaip vardų masyvo iniciatorių. Mums reikės apdailinti " = 1" . Aš nežinau puikaus būdo tai padaryti kompiliavimo metu, todėl jį atidėti iki vykdymo. Kaip rezultatas, _to_string nebus constexpr , bet _from_string vis dar gali būti constexpr , nes mes galime laikyti erdvės ir lygybės ženklai kaip terminatoriai, lyginant su nepjaustytomis stygomis.
  3. Abiem pirmiau __VA_ARGS__ reikia „atitikimo“ makro, kuris kiekvienam elementui __VA_ARGS__ gali taikyti kitą makrokomandą. Tai yra gana standartinis. Šis atsakymas apima paprastą versiją, kuri gali apdoroti iki 8 elementų.
  4. Jei makro turėtų būti tikrai savarankiška, nereikia deklaruoti statinių duomenų, kuriems reikia atskiros apibrėžties. Praktiškai tai reiškia, kad masyvams reikia specialaus apdorojimo. Yra du galimi sprendimai: „ constexpr (arba tik „ const masyvai) vardų srityje, arba įprastos matricos constexpr statinės integruotos funkcijos. Šio atsakymo kodas skirtas C ++ 11 ir naudojamas pirmasis metodas. „CodeProject“ skirtas „C ++ 98“ ir priima pastarąjį.

Kodas

 #include <cstddef> // For size_t. #include <cstring> // For strcspn, strncpy. #include <stdexcept> // For runtime_error. // A "typical" mapping macro. MAP(macro, a, b, c, ...) expands to // macro(a) macro(b) macro(c) ... // The helper macro COUNT(a, b, c, ...) expands to the number of // arguments, and IDENTITY(x) is needed to control the order of // expansion of __VA_ARGS__ on Visual C++ compilers. #define MAP(macro, ...) \ IDENTITY( \ APPLY(CHOOSE_MAP_START, COUNT(__VA_ARGS__)) \ (macro, __VA_ARGS__)) #define CHOOSE_MAP_START(count) MAP ## count #define APPLY(macro, ...) IDENTITY(macro(__VA_ARGS__)) #define IDENTITY(x) x #define MAP1(m, x) m(x) #define MAP2(m, x, ...) m(x) IDENTITY(MAP1(m, __VA_ARGS__)) #define MAP3(m, x, ...) m(x) IDENTITY(MAP2(m, __VA_ARGS__)) #define MAP4(m, x, ...) m(x) IDENTITY(MAP3(m, __VA_ARGS__)) #define MAP5(m, x, ...) m(x) IDENTITY(MAP4(m, __VA_ARGS__)) #define MAP6(m, x, ...) m(x) IDENTITY(MAP5(m, __VA_ARGS__)) #define MAP7(m, x, ...) m(x) IDENTITY(MAP6(m, __VA_ARGS__)) #define MAP8(m, x, ...) m(x) IDENTITY(MAP7(m, __VA_ARGS__)) #define EVALUATE_COUNT(_1, _2, _3, _4, _5, _6, _7, _8, count, ...) \ count #define COUNT(...) \ IDENTITY(EVALUATE_COUNT(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)) // The type "T" mentioned above that drops assignment operations. template <typename U> struct ignore_assign { constexpr explicit ignore_assign(U value) : _value(value) { } constexpr operator U() const { return _value; } constexpr const ignore_assign operator =(int dummy) const { return *this; } U _value; }; // Prepends "(ignore_assign<_underlying>)" to each argument. #define IGNORE_ASSIGN_SINGLE(e) (ignore_assign<_underlying>)e, #define IGNORE_ASSIGN(...) \ IDENTITY(MAP(IGNORE_ASSIGN_SINGLE, __VA_ARGS__)) // Stringizes each argument. #define STRINGIZE_SINGLE(e) #e, #define STRINGIZE(...) IDENTITY(MAP(STRINGIZE_SINGLE, __VA_ARGS__)) // Some helpers needed for _from_string. constexpr const char terminators[] = " =\t\r\n"; // The size of terminators includes the implicit '\0'. constexpr bool is_terminator(char c, size_t index = 0) { return index >= sizeof(terminators) ? false : c == terminators[index] ? true : is_terminator(c, index + 1); } constexpr bool matches_untrimmed(const char *untrimmed, const char *s, size_t index = 0) { return is_terminator(untrimmed[index]) ? s[index] == '\0' : s[index] != untrimmed[index] ? false : matches_untrimmed(untrimmed, s, index + 1); } // The macro proper. // // There are several "simplifications" in this implementation, for the // sake of brevity. First, we have only one viable option for declaring // constexpr arrays: at namespace scope. This probably should be done // two namespaces deep: one namespace that is likely to be unique for // our little enum "library", then inside it a namespace whose name is // based on the name of the enum to avoid collisions with other enums. // I am using only one level of nesting. // // Declaring constexpr arrays inside the struct is not viable because // they will need out-of-line definitions, which will result in // duplicate symbols when linking. This can be solved with weak // symbols, but that is compiler- and system-specific. It is not // possible to declare constexpr arrays as static variables in // constexpr functions due to the restrictions on such functions. // // Note that this prevents the use of this macro anywhere except at // namespace scope. Ironically, the C++98 version of this, which can // declare static arrays inside static member functions, is actually // more flexible in this regard. It is shown in the CodeProject // article. // // Second, for compilation performance reasons, it is best to separate // the macro into a "parametric" portion, and the portion that depends // on knowing __VA_ARGS__, and factor the former out into a template. // // Third, this code uses a default parameter in _from_string that may // be better not exposed in the public interface. #define ENUM(EnumName, Underlying, ...) \ namespace data_ ## EnumName { \ using _underlying = Underlying; \ enum { __VA_ARGS__ }; \ \ constexpr const size_t _size = \ IDENTITY(COUNT(__VA_ARGS__)); \ \ constexpr const _underlying _values[] = \ { IDENTITY(IGNORE_ASSIGN(__VA_ARGS__)) }; \ \ constexpr const char * const _raw_names[] = \ { IDENTITY(STRINGIZE(__VA_ARGS__)) }; \ } \ \ struct EnumName { \ using _underlying = Underlying; \ enum _enum : _underlying { __VA_ARGS__ }; \ \ const char * _to_string() const \ { \ for (size_t index = 0; index < data_ ## EnumName::_size; \ ++index) { \ \ if (data_ ## EnumName::_values[index] == _value) \ return _trimmed_names()[index]; \ } \ \ throw std::runtime_error("invalid value"); \ } \ \ constexpr static EnumName _from_string(const char *s, \ size_t index = 0) \ { \ return \ index >= data_ ## EnumName::_size ? \ throw std::runtime_error("invalid identifier") : \ matches_untrimmed( \ data_ ## EnumName::_raw_names[index], s) ? \ (EnumName)(_enum)data_ ## EnumName::_values[ \ index] : \ _from_string(s, index + 1); \ } \ \ EnumName() = delete; \ constexpr EnumName(_enum value) : _value(value) { } \ constexpr operator _enum() const { return (_enum)_value; } \ \ private: \ _underlying _value; \ \ static const char * const * _trimmed_names() \ { \ static char *the_names[data_ ## EnumName::_size]; \ static bool initialized = false; \ \ if (!initialized) { \ for (size_t index = 0; index < data_ ## EnumName::_size; \ ++index) { \ \ size_t length = \ std::strcspn(data_ ## EnumName::_raw_names[index],\ terminators); \ \ the_names[index] = new char[length + 1]; \ \ std::strncpy(the_names[index], \ data_ ## EnumName::_raw_names[index], \ length); \ the_names[index][length] = '\0'; \ } \ \ initialized = true; \ } \ \ return the_names; \ } \ }; 

ir taip pat

 // The code above was a "header file". This is a program that uses it. #include <iostream> #include "the_file_above.h" ENUM(Channel, char, Red = 1, Green, Blue) constexpr Channel channel = Channel::_from_string("Red"); int main() { std::cout << channel._to_string() << std::endl; switch (channel) { case Channel::Red: return 0; case Channel::Green: return 1; case Channel::Blue: return 2; } } static_assert(sizeof(Channel) == sizeof(char), ""); 

Pirmiau pateikta programa spausdina Red , kaip tikitės. Yra tam tikras tipo saugos lygis, nes negalite sukurti skaičiavimo be jo inicijavimo, o vieno iš parinkčių ištrinimas iš switch sukels kompiliatoriaus įspėjimą (priklausomai nuo jūsų kompiliatoriaus ir vėliavų). Taip pat atkreipkite dėmesį, kad kompiliavimo metu "Red" buvo konvertuota į „enum“.

80
12 июля '15 в 0:30 2015-07-12 00:30 atsakymą antron pateikė liepos 15 d., 15 val. 0:30 2015-07-12 00:30

C + + 17 C + + 20 atveju, jus domina svarstymų tyrimo grupė (SG7). Yra lygiagrečiai straipsnių serija, apimanti formuluotę ( P0194 ) ir loginį pagrindą, dizainą ir evoliuciją ( P0385 ). (Nuorodos į naujausius kiekvieno serijos straipsnius.)

Pradedant nuo P0194r2 (2016-10-15) sintaksė naudos siūlomą reflexpr raktinį žodį:

 meta::get_base_name_v< meta::get_element_m< meta::get_enumerators_m<reflexpr(MyEnum)>, 0> > 

Pavyzdžiui (pritaikytas iš Matus Choclik reflexpr filialo nuo c> ):

 #include <reflexpr> #include <iostream> enum MyEnum { AAA = 1, BBB, CCC = 99 }; int main() { auto name_of_MyEnum_0 = std::meta::get_base_name_v< std::meta::get_element_m< std::meta::get_enumerators_m<reflexpr(MyEnum)>, 0> >; // prints "AAA" std::cout << name_of_MyEnum_0 << std::endl; } 

Statinis apmąstymas negalėjo patekti į C + + 17 (o galutinis variantas, pateiktas standartų susitikime 2016 m. Lapkričio mėn. Issaqua), tačiau yra įsitikinęs, kad jis pateks į C ++ 20; iš Herb Sutter ataskaitos :

Visų pirma apmąstymų tyrimo grupė peržiūrėjo naujausią bendrą pasiūlymą dėl statinio svarstymo ir nustatė, kad yra pasirengusi prisijungti prie pagrindinių Evoliucijos grupių kitame mūsų susitikime, kad pradėtume svarstyti standartinį pasiūlymą dėl statinio atspindžio TS arba kitam standartui.

67
03 марта '15 в 14:42 2015-03-03 14:42 atsakymą pateikė ecatmur kovo 03 '15 , 14:42 2015-03-03 14:42

2011 m. Savaitgalį praleidau kurdamas makroekonominį sprendimą, todėl jo niekada nenaudojau.

Mano dabartinė procedūra yra paleisti „Vim“, nukopijuoti skaičiuotojus į tuščią jungiklio korpusą, paleisti naują makrokomandą, konvertuoti pirmąjį skaičiuoklį į atvejo ataskaitą, perkelti žymeklį į kitos eilutės pradžią, sustabdyti makrokomandą ir generuoti likusius registro operatorius, vykdant makrokomandą kitose skaičiuotojų.

„Vim“ makrokomandos yra įdomesnės nei „C ++“ makrokomandos.

Gyvenimo pavyzdys:

 enum class EtherType : uint16_t { ARP = 0x0806, IPv4 = 0x0800, VLAN = 0x8100, IPv6 = 0x86DD }; 

Aš ją sukursiu:

 std::ostream operator<< (std::ostream os, EtherType ethertype) { switch (ethertype) { case EtherType::ARP : return os << "ARP" ; case EtherType::IPv4: return os << "IPv4"; case EtherType::VLAN: return os << "VLAN"; case EtherType::IPv6: return os << "IPv6"; // omit default case to trigger compiler warning for missing cases }; return os << static_cast<std::uint16_t>(ethertype); } 

Ir taip aš einu.

Tačiau gimtoji enum enum parama būtų daug geriau. Labai džiaugiuosi matydamas darbo grupės dėl apmąstymų C + + 17 rezultatus.

Alternatyvus būdas tai padaryti buvo paskelbtas @sehe komentaruose .

15
12 марта '15 в 2:01 2015-03-12 02:01 Atsakymas duotas StackedCrooked Kovo 12 '15 15:01 2015-03-12 02:01

Nežinau, ar jums tai patiks, ar ne, aš ne labai džiaugiuosi šiuo sprendimu, bet tai yra draugiškas požiūris su C + + 14, nes jis naudoja šablonų kintamuosius ir piktnaudžiauja šablono specializacija:

 enum class MyEnum : std::uint_fast8_t { AAA, BBB, CCC, }; template<MyEnum> const char MyEnumName[] = "Invalid MyEnum value"; template<> const char MyEnumName<MyEnum::AAA>[] = "AAA"; template<> const char MyEnumName<MyEnum::BBB>[] = "BBB"; template<> const char MyEnumName<MyEnum::CCC>[] = "CCC"; int main() { // Prints "AAA" std::cout << MyEnumName<MyEnum::AAA> << '\n'; // Prints "Invalid MyEnum value" std::cout << MyEnumName<static_cast<MyEnum>(0x12345678)> << '\n'; // Well... in fact it prints "Invalid MyEnum value" for any value // different of MyEnum::AAA, MyEnum::BBB or MyEnum::CCC. return 0; } 

Blogiausias dalykas dėl šio požiūrio yra skausmas, kurį reikia išlaikyti, bet taip pat skauda remti kai kuriuos kitus panašius testus, ar ne?

Geras taškas apie šį sprendimą:

  • Kintamų tempatų naudojimas (C ++ funkcija 14)
  • Su šablono specializacijos pagalba mes galime „aptikti“, kai naudojama negaliojanti vertė (bet aš nesu įsitikinęs, kad jis gali būti naudingas).
  • Jis atrodo tvarkingas.
  • Vardų peržiūra atliekama kompiliavimo metu.

Realaus laiko pavyzdys

Keisti

Neteisingas user673679 esate teisus; Kintamojo C ++ 14 šablonas neatitinka vykdymo bylos, mano klaida yra pamiršti: (

Tačiau mes vis dar galime naudoti kai kurias pažangias C ++ funkcijas ir šablono šabloną, taip pat chaotišką modelį, kad būtų pasiekta runtime transliacija nuo enum vertės iki eilutės ... tai taip pat erzina kaip ir kiti, bet verta paminėti.

Pradėkime naudoti šablono slapyvardį, kad sumažintume prieigą prie sąrašo kortelės kiekvienoje eilutėje:

 // enum_map contains pairs of enum value and value string for each enum // this shortcut allows us to use enum_map<whatever>. template <typename ENUM> using enum_map = std::map<ENUM, const std::string>; // This variable template will create a map for each enum type which is // instantiated with. template <typename ENUM> enum_map<ENUM> enum_values{};