【问题标题】:How can I output the value of an enum class in C++11如何在 C++11 中输出枚举类的值
【发布时间】:2012-07-10 09:48:31
【问题描述】:

如何在 C++11 中输出 enum class 的值?在 C++03 中是这样的:

#include <iostream>

using namespace std;

enum A {
  a = 1,
  b = 69,
  c= 666
};

int main () {
  A a = A::c;
  cout << a << endl;
}

在 c++0x 中,此代码无法编译

#include <iostream>

using namespace std;

enum class A {
  a = 1,
  b = 69,
  c= 666
};

int main () {
  A a = A::c;
  cout << a << endl;
}


prog.cpp:13:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&'
/usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../../include/c++/4.5.1/ostream:579:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = A]'

编译于Ideone.com

【问题讨论】:

  • 为什么要输出枚举? enum 类用于不将枚举值与 int 表示混淆
  • @RiaD 如果您在 switch 语句中评估枚举类值并希望在默认分支中捕获未处理的值怎么办,例如打印未处理的值?
  • @stackprotector 好吧,在过去的 9 年里,我学到了一些需要它的用例 :) 当然,你的用例是其中的主要部分

标签: c++ c++11 templates enums enum-class


【解决方案1】:

与无范围枚举不同,范围枚举不能隐式转换为其整数值。您需要显式使用强制转换将其转换为整数:

std::cout << static_cast<std::underlying_type<A>::type>(a) << std::endl;

您可能希望将逻辑封装到函数模板中:

template <typename Enumeration>
auto as_integer(Enumeration const value)
    -> typename std::underlying_type<Enumeration>::type
{
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

用作:

std::cout << as_integer(a) << std::endl;

【讨论】:

【解决方案2】:
#include <iostream>
#include <type_traits>

using namespace std;

enum class A {
  a = 1,
  b = 69,
  c= 666
};

std::ostream& operator << (std::ostream& os, const A& obj)
{
   os << static_cast<std::underlying_type<A>::type>(obj);
   return os;
}

int main () {
  A a = A::c;
  cout << a << endl;
}

【讨论】:

  • 我逐字复制了这个示例并将其编译为g++ -std=c++0x enum.cpp,但我得到了一堆编译器错误->pastebin.com/JAtLXan9。我也无法从@james-mcnellis 获取示例进行编译。
  • @Dennis underlying_type 仅在 C++11 中
  • @Deqing 与enum class相同。
【解决方案3】:

可以让您的第二个示例(即使用范围枚举的示例)使用与无范围枚举相同的语法来工作。此外,该解决方案是通用的,适用于所有作用域枚举,而不是为每个作用域枚举编写代码(如@ForEveR 提供的answer 所示)。

解决方案是编写一个通用的operator&lt;&lt; 函数,它适用于任何范围的枚举。解决方案通过std::enable_if使用SFINAE,如下。

#include <iostream>
#include <type_traits>

// Scoped enum
enum class Color
{
    Red,
    Green,
    Blue
};

// Unscoped enum
enum Orientation
{
    Horizontal,
    Vertical
};

// Another scoped enum
enum class ExecStatus
{
    Idle,
    Started,
    Running
};

template<typename T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
{
    return stream << static_cast<typename std::underlying_type<T>::type>(e);
}

int main()
{
    std::cout << Color::Blue << "\n";
    std::cout << Vertical << "\n";
    std::cout << ExecStatus::Running << "\n";
    return 0;
}

【讨论】:

  • std::underlying_type&lt;T&gt;::type 之前需要一个typename
  • @uckelman 你是绝对正确的。感谢您更新我的答案。
  • 这在 clang 下对我有用,但在 gcc 4.9.2 下,将 error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’。这似乎是因为当流是临时的时,ADL 失败,上面的模板是不可能的。有什么建议吗?
  • @ofloveandhate 您能否提供一个指向产生该问题的示例的链接?我在 gcc 4.9.2 中测试了上面的代码,没有任何问题,只有轻微的变化,我通过将 &lt;&lt; 运算符链接在一起,将 3 个 cout 语句转换为单个 cout 语句。见here
  • 让我修改一下我的陈述。我试图从该类外部打印一个包含在一个类中的枚举类。上面的代码确实适用于不包含在类本身中的枚举类。
【解决方案4】:

为了写得更简单,

enum class Color
{
    Red = 1,
    Green = 11,
    Blue = 111
};

int value = static_cast<int>(Color::Blue); // 111

【讨论】:

  • 当枚举被显式地赋予底层类型时,这将不起作用
【解决方案5】:

(我还不能发表评论。)我建议对 James McNellis 已经很好的答案进行以下改进:

template <typename Enumeration>
constexpr auto as_integer(Enumeration const value)
    -> typename std::underlying_type<Enumeration>::type
{
    static_assert(std::is_enum<Enumeration>::value, "parameter is not of type enum or enum class");
    return static_cast<typename std::underlying_type<Enumeration>::type>(value);
}

  • constexpr: 允许我使用枚举成员值作为编译时数组大小
  • static_assert+is_enum:“确保”函数在编译时完成某事。按照建议,仅使用枚举

顺便问一下自己:当我想为我的枚举成员分配数字值时,为什么要使用enum class?!考虑转换工作。

也许我会回到普通的enum,就像我在这里建议的那样:How to use enums as flags in C++?


根据@TobySpeight 的建议,没有 static_assert 的另一种(更好的)风格:

template <typename Enumeration>
constexpr std::enable_if_t<std::is_enum<Enumeration>::value,
std::underlying_type_t<Enumeration>> as_number(const Enumeration value)
{
    return static_cast<std::underlying_type_t<Enumeration>>(value);
}

【讨论】:

  • 是否存在 T 的类型 std::underlying_type&lt;T&gt;::type 存在,但 std::is_enum&lt;T&gt;::value 为假?如果没有,那么static_assert 不会增加任何价值。
  • 我没有在所有编译器上进行测试。但是,@TobySpeight 你可能是对的,msvc2013 似乎吐出了可理解的错误消息,表明存在的基础类型与枚举类型之间存在一对一的对应关系。而且 static_assert 甚至没有被解雇。但是:参考资料说,如果没有提供完整的枚举类型,则底层类型的行为是未定义的。所以 static_assert 只是希望获得最大程度的可理解信息以防万一。也许有可能强制它更早/最早得到处理?
  • 啊,是的,如果Enumeration 不是一个完整的枚举类型,它是未定义的,这是对的。在这种情况下,它可能已经为时已晚,因为它在返回类型中使用。也许我们可以指定std::enable_if&lt;std::is_enum&lt;Enumeration&gt;::value, std::underlying_type&lt;Enumeration&gt;::type&gt; 作为返回类型?当然,如果你有一个支持概念的编译器,那就更容易了(而且错误消息也更清晰了)......
【解决方案6】:

以下在 C++11 中对我有用:

template <typename Enum>
constexpr typename std::enable_if<std::is_enum<Enum>::value,
                                  typename std::underlying_type<Enum>::type>::type
to_integral(Enum const& value) {
    return static_cast<typename std::underlying_type<Enum>::type>(value);
}

【讨论】:

    【解决方案7】:

    c++23 起有

    std::to_underlying 与所有其他答案的作用相同,但它在 std

    【讨论】:

    • 应该举个例子。
    • static_cast&lt;typename std::underlying_type&lt;T&gt;::type&gt;(e);std::to_underlying(e); 相同
    • ...我在答案中明确表示
    【解决方案8】:

    你可以这样做:

    //outside of main
    namespace A
    {
        enum A
        {
            a = 0,
            b = 69,
            c = 666
        };
    };
    
    //in main:
    
    A::A a = A::c;
    std::cout << a << std::endl;
    

    【讨论】:

    • 关于枚举类的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多