现代C++11/C++14/C++17及未来的C++20中如何将枚举类型转换为字符串

551
与所有其他类似的问题相反,这个问题是关于使用新的C++特性的。

阅读了许多答案,但仍未找到以下内容:

  • 使用C++11C++14C++17的新特性的优雅方法
  • 或者在Boost中已经有现成的东西可用
  • 另外,还有一些计划在C++20中实现的东西

示例

一个例子胜过千言万语的解释。
你可以在Coliru上编译和运行这段代码片段。
另一个之前的示例也可用)

#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's 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';
}

约束条件

  • 请勿无意义地复制其他答案基本链接
  • 请避免使用臃肿的宏,或尽可能减少#define的开销。
  • 请勿手动进行enumstring的映射。

最好有的

  • 支持从非零数字开始的枚举
  • 支持负枚举
  • 支持分段枚举
  • 支持类枚举(C++11)
  • 支持任何允许的<type>class enum: <type>(C++11)
  • 编译时(而非运行时)将枚举转换为字符串,或者至少在运行时快速执行(例如,std::map不是一个好主意...)
  • constexpr(C++11,然后放宽到C++14/17/20)
  • noexcept(C++11)
  • C++17/C++20友好代码片段

一个可能的想法是利用C++编译器的能力,通过基于可变参数模板类和constexpr函数的元编程技巧,在编译时生成C++代码...


4
(可能与主题无关)看看这个与Qt相关的博客。http://woboq.com/blog/reflection-in-cpp-and-qt-moc.html。描述了使用C++反射(拟议中的标准)替换Qt的moc(元对象编译器)的可能性。 - ibre5041
17
N4113std::enumerator::identifier_v<MyEnum, MyEnum::AAA> - ecatmur
3
所有的问题都必须用C++解决吗?自动生成字符串表示代码非常容易,只需要几行代码。 - Karoly Horvath
6
如果可能的话,请不要提供基于 C 宏的答案。除非您愿意等待 C++17,否则几乎没有可用的东西,将枚举声明为DEC_ENUM(enumname, (a,b,c,(d,b),(e,42)))并不是那么糟糕,除非您必须维护生成宏... 在我看来,将这种情况放入语言中只是另一种在缺少更强大的模板/宏混合体的情况下的替代方法。我们不应该为了能够说宏不再有用而将所有这些有用的宏用例添加到语言中。 - PlasmaHH
2
@olibre,这个问题至少有两个今天可用的答案。1. @ecatmur关于C++17的不错回答,我们不能每次C++17讨论更新时都进行编辑。请参阅反射研究小组的邮件列表。2. 我的回答提供了当前C++的不错语法,被许多人在生产中使用,但在内部使用#define。你所问的是一个可用的解决方案。今天的正确答案是,在以后没有完全“正确”的解决方案之前(即现在接受@ecatmur)。 - antron
显示剩余21条评论
32个回答

0

好的,又是另一种选择。典型用例是您需要 HTTP 动词的常量以及使用其字符串版本值。

例如:

int main () {

  VERB a = VERB::GET;
  VERB b = VERB::GET;
  VERB c = VERB::POST;
  VERB d = VERB::PUT;
  VERB e = VERB::DELETE;


  std::cout << a.toString() << std::endl;

  std::cout << a << std::endl;

  if ( a == VERB::GET ) {
    std::cout << "yes" << std::endl;
  }

  if ( a == b ) {
    std::cout << "yes" << std::endl;
  }

  if ( a != c ) {
    std::cout << "no" << std::endl;
  }

}

VERB类:

// -----------------------------------------------------------
// -----------------------------------------------------------
class VERB {

private:

  // private constants
  enum Verb {GET_=0, POST_, PUT_, DELETE_};

  // private string values
  static const std::string theStrings[];

  // private value
  const Verb value;
  const std::string text;

  // private constructor
  VERB (Verb v) :
  value(v), text (theStrings[v])
  {
    // std::cout << " constructor \n";
  }

public:

  operator const char * ()  const { return text.c_str(); }

  operator const std::string ()  const { return text; }

  const std::string toString () const { return text; }

  bool operator == (const VERB & other) const { return (*this).value == other.value; }

  bool operator != (const VERB & other) const { return ! ( (*this) == other); }

  // ---

  static const VERB GET;
  static const VERB POST;
  static const VERB PUT;
  static const VERB DELETE;

};

const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"};

const VERB VERB::GET = VERB ( VERB::Verb::GET_ );
const VERB VERB::POST = VERB ( VERB::Verb::POST_ );
const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ );
const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ );
// end of file

1
为了减少内存使用量,您可以用theStrings[v]替换成员const std::string text。然而,问题是关于C++11/C++14/C++17/C++20的功能,以避免手动编写这样的类 :-/ - oHo

-4
你可以使用 select() 函数,它实际上只是一种简写的开关;它并不是真正意义上的解决方案,但它确实让生活变得更加轻松:
enum
{
  NORMAL,
  INVALID
} state(NORMAL);

//std::cout << (state ? "INVALID" : "NORMAL") << std::endl;
std::cout << select(state, "NORMAL", "INVALID") << std::endl;

select()函数在SIMD/GPU编程中很常见。它们是三元?:运算符的概括。你也可以将select()视为一个函数数组(实现数组数据结构的函数)。

这里有一个完整的示例

编辑:select()不需要复杂,这里有一个简单的:

char const* select(std::size_t const i, char const* const (&a)[]) { return a[i]; }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接