如何在C ++ 11中输出枚举类的值


96

如何enum class在C ++ 11中输出an的值?在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上编译


1
为什么要尝试输出枚举?枚举类用于不将枚举值与int表示相
混淆

Answers:


122

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

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;

3
是否有使用尾随返回类型语法的原因?
Nicol Bolas 2012年

3
@NicolBolas:我as_integer从一个开放源代码库CxxReflect中复制了代码(请参阅enumeration.hpp)。该库在任何地方都一致地使用尾随返回类型。为了一致性。
James McNellis 2012年

11
尽管已经晚了2年,但如果有人看到此问题,您可以使用上面的强制转换方法,只需调用“ static_cast <int>(value)”即可将整数或“ static_cast <A>(intValue)”转换为获得一个枚举值。请记住,从int到enum或从enum到enum可能会引起问题,并且通常是设计错误的标志。
Benjamin Danger Johnson

4
int(value)和A(intValue)也可以使用,没有难看的尖括号。
Grault 2014年

4
as_integer可以定义为,constexpr以便可以在需要常量表达式的上下文中使用。
纳瓦兹2015年

39
#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获取示例进行编译。
丹尼斯

4
@Dennis underlying_type仅在C ++ 11
德清

23

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

解决方案是编写一个通用operator<<函数,该函数将适用于任何范围枚举。该解决方案通过和使用SFINAEstd::enable_if,如下所示。

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

您需要一个typename之前std::underlying_type<T>::type
uckelman

@uckelman你是完全正确的。感谢您更新我的答案。
James Adkison

这在clang下对我有用,但是在gcc 4.9.2下,将<<链接到一起时,此解决方案失败,并显示错误error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’。这似乎是因为当流是临时的时,ADL失败了,并且上面的模板是不可能的。有小费吗?
ofloveandhate 2015年

@ofloveandhate您能否提供导致该问题的示例的链接?我在gcc 4.9.2中测试了上面的代码,没有任何问题,只有一点点改变,我通过将运算符链接在一起将3条cout语句转换为一条cout语句<<。看到这里
James Adkison

让我修改我的陈述。我试图从该类外部打印包含在该类内部的枚举类。上面的代码确实适用于类本身不包含的枚举类。
ofloveandhate 2015年

10

(我不允许发表评论。)我建议对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像我在这里建议的那样回到普通状态:如何在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<T>::type存在的,但是std::is_enum<T>::value是假的?如果没有,则static_assert没有任何价值。
Toby Speight

1
我没有在所有编译器上进行测试。但是,@ TobySpeight您可能是对的,msvc2013似乎会吐出可理解的错误消息,表明存在的underlying_type_t与枚举类型本身之间存在1对1的对应关系。而且甚至不会触发static_assert。但是:参考文献指出,如果未提供完整的枚举类型,则underlying_type的行为是不确定的。因此,static_assert只是希望获得最大程度的可理解消息,以防万一。也许有可能迫使它早/早处理?
yau

嗯,是的,如果Enumeration不是完整的枚举类型,那么它是未定义的,这是正确的。在这种情况下,可能已经为时已晚,因为它已在返回类型中使用。也许我们可以指定std::enable_if<std::is_enum<Enumeration>::value, std::underlying_type<Enumeration>::type>返回类型?当然,如果您具有支持Concepts的编译器,那么操作会容易得多(错误消息更加清晰)...
Toby Speight


3

以下在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);
}

0

您可以执行以下操作:

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

2
该问题询问一个枚举类。
蚂蚁
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.