枚举与强类型枚举

我是 C + + 编程的初学者。

今天我遇到了一个新的话题: 强类型 enum。我已经研究了它一点,但直到现在我还不能找出为什么我们需要这个,有什么用处呢?

例如:

enum xyz{a, b, c};
/*a = 0, b = 1, c = 2, (Typical C format)*/

为什么我们需要写:

enum class xyz{a, b, c};

我们到底想干什么? 我最大的疑问是如何使用它。 你能不能举个小例子,让我明白。

64851 次浏览

Values of enum class is really of type enum class, not underlying_type as for C-enums.

enum xyz { a, b, c};
enum class xyz_c { d, f, e };


void f(xyz x)
{
}


void f_c(xyz_c x)
{
}


// OK.
f(0);
// OK for C++03 and C++11.
f(a);
// OK with C++11.
f(xyz::a);
// ERROR.
f_c(0);
// OK.
f_c(xyz_c::d);

OK, first example: old-style enums do not have their own scope:

enum Animals {Bear, Cat, Chicken};
enum Birds {Eagle, Duck, Chicken}; // error! Chicken has already been declared!


enum class Fruits { Apple, Pear, Orange };
enum class Colours { Blue, White, Orange }; // no problem!

Second, they implicitly convert to integral types, which can lead to strange behaviour:

bool b = Bear && Duck; // what?

Finally, you can specify the underlying integral type of C++11 enums:

enum class Foo : char { A, B, C};

Previously, the underlying type was not specified, which could cause compatibility problems between platforms. Edit It has been pointed out in comments that you can also specify the underlying integral type of an "old style" enum in C++11.

There's a good article about enums at this IBM page, it's very detailed and well-written. Here are some important points in a nutshell:

The scoped enums solve most of the limitations incurred by regular enums: complete type safety, well-defined underlying type, scope issues, and forward declaration.

  • You get type safety by disallowing all implicit conversions of scoped enums to other types.
  • You get a new scope, and the enum is not anymore in the enclosing scope, saving itself from name conflicts.
  • Scoped enums gives you the ability to specify the underlying type of the enumeration, and for scoped enums, it defaults to int if you choose not to specify it.
  • Any enum with a fixed underlying type can be forward declared.

Enum Scope

Enumerations export their enumerators to the surrounding scope. This has two drawbacks. First, it can lead to name clashes, if two enumerators in different enums declared in the same scope have the same name; second, it's not possible to use an enumerator with a fully qualified name, including the enum name.

enum ESet {a0, a, a1, b1, c3};
enum EAlpha{a, b, c}


select = ESet::a; // error
select = a;       // is ambigious

The enum classes ("new enums", "strong enums") address three problems with traditional C++ enumerations:

  1. conventional enums implicitly convert to int, causing errors when someone does not want an enumeration to act as an integer.
  2. conventional enums export their enumerators to the surrounding scope, causing name clashes.
  3. The underlying type of an enum cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible.

enum class ("strong enums") are strongly typed and scoped:

enum Alert { green, yellow, orange, red }; // traditional enum


enum class Color { red, blue };   // scoped and strongly typed enum
// no export of enumerator names into enclosing scope
// no implicit conversion to int
enum class TrafficLight { red, yellow, green };


Alert a = 7;              // error (as ever in C++)
Color c = 7;              // error: no int->Color conversion


int a2 = red;             // ok: Alert->int conversion
int a3 = Alert::red;      // error in C++98; ok in C++11
int a4 = blue;            // error: blue not in scope
int a5 = Color::blue;     // error: not Color->int conversion


Color a6 = Color::blue;   // ok

As shown, traditional enums work as usual, but you can now optionally qualify with the enum's name.

The new enums are "enum class" because they combine aspects of traditional enumerations (names values) with aspects of classes (scoped members and absence of conversions).

Being able to specify the underlying type allow simpler interoperability and guaranteed sizes of enumerations:

enum class Color : char { red, blue };  // compact representation


enum class TrafficLight { red, yellow, green };  // by default, the underlying type is int


enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U };   // how big is an E?
// (whatever the old rules say;
// i.e. "implementation defined")


enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U };   // now we can be specific

It also enables forward declaration of enums:

enum class Color_code : char;     // (forward) declaration
void foobar(Color_code* p);       // use of forward declaration
// ...
enum class Color_code : char { red, yellow, green, blue }; // definition

The underlying type must be one of the signed or unsigned integer types; the default is int.

In the standard library, enum classes are used for:

  1. Mapping systems specific error codes: In <system_error>: enum class errc;
  2. Pointer safety indicators: In <memory>: enum class pointer_safety { relaxed, preferred, strict };
  3. I/O stream errors: In <iosfwd>: enum class io_errc { stream = 1 };
  4. Asynchronous communications error handling: In <future>: enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

Several of these have operators, such as == defined.