本文介绍了在现代C ++和未来的C ++ 17 / C ++ 20中枚举到字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 限时删除!! 又一个重复出现的问题 2008 c 在C中以字符串形式使用枚举类型的变量的简单方法? 2008 c ++ 如何轻松地将c ++枚举映射到字符串 2008 c ++ 使某个内容标识符和字符串? 2008 c ++ 是否有简单的脚本将C ++枚举转换为字符串? 2009 c ++ 如何在C ++中使用枚举作为标志? 2011 c ++ 如何将枚举类型变量转换为字符串? 2011 c ++ String to C ++ 2011 c ++ 如何转换枚举类型变量到字符串? 2012 c 如何在c中将枚举名称转换为字符串 2013 c 在C中枚举有条件编译的枚举 但是在阅读了很多答案之后,我还没有找到任何: 使用 C ++ 11 / C ++ 14 新功能 或者 Boost 中随时可用的内容 其他计划的内容for C ++ 17 示例 一个例子通常比长解释更好。 您可以在 Coliru 。 (另一个前例子也可用) #include< ; map> #include< iostream> struct MyClass { enum class MyEnum:char { AAA = -8, BBB ='8', CCC = AAA + BBB }; }; //用一些更快的编译时生成的代码替换magic() auto 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()? 超出范围:it->第二; } int main() { std :: cout< magic(MyClass :: MyEnum :: AAA)<<'\\\'; std :: cout<< magic(MyClass :: MyEnum :: BBB)<''\\\'; std :: cout<< magic(MyClass :: MyEnum :: CCC)<<'\\\'; } 约束 请勿重复其他答案或基本链接。 请避免以膨胀为基础的回答,或尝试将 #define 的开销降至最低。 请不要手动枚举 - > 字符串映射。 很高兴有 支持枚举从不同于零的数字开始的值 支持负数枚举 b $ b 支持碎片枚举值 支持 class enum (C ++ 11) 支持 class enum:< type> 允许< type& ; 函数(或变量) $ c> constexpr (C ++ 11,在C ++ 14中放宽) 函数 noexcept (C ++ 11) 在运行时快速执行(即 std :: map 不是最好的想法) li> 最新状态和代码片段C ++ 14友好(或更高版本)将会赞赏: - ) 一个可能的想法是使用C ++编译器功能在编译时使用元编程技巧来生成C ++代码 variadic模板类和 constexpr 函数... 解决方案有一种方法可以对当前C ++中的字符串进行枚举,如下所示: ENUM(Channel,char,Red = 1,Green,Blue) //Same as: //枚举类Channel:char {Red = 1,Green,Blue} ; 用法: code> Channel c = Channel :: _ from_string(Green); // Channel :: Green(2) c._to_string(); // stringGreen for(Channel c:Channel :: _ values()) std :: cout< c<< std :: endl; //等等... constexpr 。您还可以实现@ecatmur的答案中提到的C ++ 17反射建议。 只有一个宏。我相信这是最小的可能,因为预处理程序字符串化(#)是将令牌转换为当前C ++中的字符串的唯一方法。 宏是相当不引人注目的 - 常量声明,包括初始化器,被粘贴到内置的枚举声明。 重复已消除。 实施是最自然和有用的在至少C ++ 11中,由于 constexpr 。也可以使用C ++ 98 + __ VA_ARGS __ 。它是绝对现代的C ++。 宏的定义有些涉及, m回答这个在几个方面。 这个答案的大部分是一个实现,我认为是适合StackOverflow的空间约束。 / li> 还有 CodeProject文章,描述了长篇教程中实现的基础知识。 [我应该把它移到这里吗? ]。 有一个已满-featured库Better Enums在单个头文件中实现宏。它还实现 N4428类型属性查询,当前版本C ++ 17反射提议N4113。所以,至少对于通过这个宏声明的枚举,你可以在C ++ 11 / C ++ 14中建立C ++ 17枚举反射。 直接将这个答案扩展到库的特性 - 在这里没有什么重要。 免责声明:我是CodeProject文章和作者的作者, 您可以尝试此答案中的代码, 图书库和实施的N4428 在线在线。库文档还包含如何将其用作N4428的概述,其中解释了该提案的枚举部分。 说明 下面的代码实现枚举和字符串之间的转换。然而,它可以扩展到做其他事情,如迭代。这个回答在 struct 中包含一个枚举。您还可以在枚举旁边生成一个traits struct 。 策略是生成如下内容: / p> 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){/ * easy * /} constexpr static通道_from_string(const char * s){/ * easy * /} } ; 问题是: 我们最终会得到 {Red = 1,Green,Blue} 作为values数组的初始值。这不是有效的C ++,因为 Red 不是可分配的表达式。这可以通过将每个常量转换为具有赋值运算符的 T 类型来解决,但会删除赋值: {(T)Red = 1,同样,我们将最终以 {Red = 1,T,绿色,蓝色} 作为names数组的初始化器。我们需要修剪= 1。我不知道一个伟大的方式来做这个在编译时,所以我们将推迟这是运行时间。因此, _to_string 不会是 constexpr ,而是 _from_string 仍然可以是 constexpr ,因为我们可以将空格和等号作为终止符,与未修饰的字符串进行比较。 需要一个映射宏,可以对 __ VA_ARGS __ 中的每个元素应用另一个宏。这是很标准的。这个答案包括一个简单的版本,可以处理多达8个元素。 如果宏是真正自包含的,它需要声明没有需要单独定义的静态数据。在实践中,这意味着数组需要特殊处理。在命名空间范围有两个可能的解决方案: constexpr (或只是 const code> constexpr 静态内联函数。这个答案中的代码是针对C ++ 11并采用前一种方法。 CodeProject的文章是针对C ++ 98的,并采用后者。 / h1> #include< cstddef> // For size_t。 #include< cstring> // For strcspn,strncpy。 #include< stdexcept> // For runtime_error。 //典型映射宏。 MAP(宏,a,b,c,...)扩展为 //宏(a)宏(b)宏(c)... //帮助宏COUNT b,c,...)扩展为 //参数的数量,并且需要IDENTITY(x)来控制的顺序//在Visual C ++编译器上扩展__VA_ARGS__。 #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 (m,__VA_ARGS__)) #define MAP3(m,x,...)m(x)IDENTITY(MAP2(m,__VA_ARGS__)) #define MAP4 )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 ) #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)) //上面提到的类型T 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; }; //将(ignore_assign< _underlying>)添加到每个参数。 #define IGNORE_ASSIGN_SINGLE(e)(ignore_assign< _underlying>)e, #define IGNORE_ASSIGN(...)\ IDENTITY(MAP(IGNORE_ASSIGN_SINGLE,__VA_ARGS__)) //对每个参数进行字符串化。 #define STRINGIZE_SINGLE(e)#e, #define STRINGIZE(...)IDENTITY(MAP(STRINGIZE_SINGLE,__VA_ARGS__)) // _from_string需要一些辅助函数。 constexpr const char terminators [] == \t\r\\\; //终结符的大小包括隐式的'\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); } //宏适用。 // //在这个实现中有几个简化,对于 //为了简洁。首先,我们只有一个可行的选项来声明 // constexpr arrays:at namespace scope。这可能是应该做的 //两个命名空间深:一个命名空间可能是唯一的 //我们的小枚举库,然后在其内部命名空间的名称为 / /基于枚举的名称,以避免与其他枚举的冲突。 //我只使用一级嵌套。 // //在结构体中声明constexpr数组是不可行的,因为 //它们需要超出行的定义,这将导致 //重复的符号当链接时。这可以用弱 //符号来解决,但这是编译器和系统特定的。它不是 //可能在 // constexpr函数中声明constexpr数组为静态变量,因为这些函数的限制。 // //注意,除非在 //命名空间范围,否则这将阻止使用此宏。讽刺的是,这个C ++ 98版本,它可以 //在静态成员函数中声明静态数组,实际上是 //在这方面更灵活。它显示在CodeProject //文章中。 // //第二,为了编译性能的原因,最好将宏 //分成一个参数部分,并且依赖的部分 //知道__VA_ARGS__,并将前者换成模板。 // //第三,此代码在_from_string中使用默认参数,可能会更好地暴露在公共接口中的 //。 #define ENUM(EnumName,Underlying,...)\ 命名空间data_ ## EnumName {\ using _underlying = Underlying; \ 枚举{__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指数]; \ } \ \ 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; \ ++索引){\ \ 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; \ } \ }; 和 c $ c> //上面的代码是一个头文件。这是一个使用它的程序。 #include< iostream> #includethe_file_above.h ENUM(Channel,char,Red = 1,Green,Blue) constexpr通道通道= Channel :: _ from_string 红); int main() { std :: cout< channel._to_string()<< std :: endl; 开关(通道){ case Channel :: Red:return 0; case Channel :: Green:return 1; case Channel :: Blue:return 2; } } static_assert(sizeof(Channel)== sizeof(char),); 上面的程序打印 Red 会期望。有一定程度的类型安全性,因为你不能创建枚举而没有初始化它,并从开关删除其中一个情况将导致编译器的警告(取决于你的编译器和标志)。此外,请注意,Red在编译期间已转换为枚举。 Yet another recurrent duplicated question2008 c Easy way to use variables of enum types as string in C?2008 c++ How to easily map c++ enums to strings2008 c++ Making something both a C identifier and a string?2008 c++ Is there a simple script to convert C++ enum to string?2009 c++ How to use enums as flags in C++?2011 c++ How to convert an enum type variable to a string?2011 c++ Enum to String C++2011 c++ How to convert an enum type variable to a string?2012 c How to convert enum names to string in c2013 c Stringifying an conditionally compiled enum in CBut after reading many answers, I did not yet find any:Elegant way using C++11/C++14 new featuresOr something ready-to-use in BoostElse something planned for C++17ExampleAn example is often better than a long explanation.You can compile and run this snippet on Coliru.(Another former example is also available)#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 codeauto 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';}ConstraintsPlease no invaluable duplication of other answers or basic link.Please avoid bloat macro-based answer, or try to reduce the #define overhead as minimum as possible.Please no manual enum -> string mapping.Nice to haveSupport enum values starting from a number different from zeroSupport negative enum valuesSupport fragmented enum valuesSupport class enum (C++11)Support class enum : <type> having any allowed <type> (C++11)Code generated at compile-timeFunction (or variable) constexpr (C++11, relaxed in C++14)Function noexcept (C++11)Fast execution at run-time (i.e. std::map is not the best idea)State of the art and snippet C++14 friendly (or beyond) will be appreciated :-)One possible idea could be using the C++ compiler capabilities to generate C++ code at compilation using meta-programming tricks based on variadic template class and constexpr functions... 解决方案 There is a way to do enum to string in current C++ that looks like this:ENUM(Channel, char, Red = 1, Green, Blue)// "Same as":// enum class Channel : char { Red = 1, Green, Blue };Usage: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...All operations can be made constexpr. You can also implement the C++17 reflection proposal mentioned in the answer by @ecatmur.There is only one macro. I believe this is the minimum possible, because preprocessor stringization (#) is the only way to convert a token to a string in current C++.The macro is pretty unobtrusive – the constant declarations, including initializers, are pasted into a built-in enum declaration. This means they have the same syntax and meaning as in a built-in enum.Repetition is eliminated.The implementation is most natural and useful in at least C++11, due to constexpr. It can also be made to work with C++98 + __VA_ARGS__. It is definitely modern C++.The macro's definition is somewhat involved, so I'm answering this in several ways.The bulk of this answer is an implementation that I think is suitable for the space constraints on StackOverflow.There is also a CodeProject article describing the basics of the implementation in a long-form tutorial. [Should I move it here? I think it's too much for a SO answer].There is a full-featured library "Better Enums" that implements the macro in a single header file. It also implements N4428 Type Property Queries, the current revision of the C++17 reflection proposal N4113. So, at least for enums declared through this macro, you can have the proposed C++17 enum reflection now, in C++11/C++14.It is straightforward to extend this answer to the features of the library – nothing "important" is left out here. It is, however, quite tedious, and there are compiler portability concerns.Disclaimer: I am the author of both the CodeProject article and the library.You can try the code in this answer, the library, and the implementation of N4428 live online in Wandbox. The library documentation also contains an overview of how to use it as N4428, which explains the enums portion of that proposal.ExplanationThe code below implements conversions between enums and strings. However, it can be extended to do other things as well, such as iteration. This answer wraps an enum in a struct. You can also generate a traits struct alongside an enum instead.The strategy is to generate something like this: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) { /* easy */ } constexpr static Channel _from_string(const char *s) { /* easy */ }};The problems are:We will end up with something like {Red = 1, Green, Blue} as the initializer for the values array. This is not valid C++, because Red is not an assignable expression. This is solved by casting each constant to a type T that has an assignment operator, but will drop the assignment: {(T)Red = 1, (T)Green, (T)Blue}.Similarly, we will end up with {"Red = 1", "Green", "Blue"} as the initializer for the names array. We will need to trim off the " = 1". I am not aware of a great way to do this at compile time, so we will defer this to run time. As a result, _to_string won't be constexpr, but _from_string can still be constexpr, because we can treat whitespace and equals signs as terminators when comparing with untrimmed strings.Both the above need a "mapping" macro that can apply another macro to each element in __VA_ARGS__. This is pretty standard. This answer includes a simple version that can handle up to 8 elements.If the macro is to be truly self-contained, it needs to declare no static data that requires a separate definition. In practice, this means arrays need special treatment. There are two possible solutions: constexpr (or just const) arrays at namespace scope, or regular arrays in non-constexpr static inline functions. The code in this answer is for C++11 and takes the former approach. The CodeProject article is for C++98 and takes the latter.Code#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; \ } \};and// 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), "");The program above prints Red, as you would expect. There is a degree of type safety, since you can't create an enum without initializing it, and deleting one of the cases from the switch will result in a warning from the compiler (depending on your compiler and flags). Also, note that "Red" was converted to an enum during compilation. 这篇关于在现代C ++和未来的C ++ 17 / C ++ 20中枚举到字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 1403页,肝出来的.. 09-06 10:08