C++11 语言扩展 — 其他类型
枚举类 (enum class)
enum class
(“新枚举”、“强枚举”)解决了传统 C++ 枚举的三个问题:
- 传统
enum
会隐式转换为整数,当不希望枚举表现为整数时,这会导致错误。 - 传统
enum
将其枚举器导出到周围作用域,导致名称冲突。 enum
的底层类型无法指定,导致混淆、兼容性问题,并使前向声明成为不可能。
enum class
(“强枚举”)是强类型且带作用域的。
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
如所示,传统的 enum
仍然像往常一样工作,但您现在可以选择使用 enum
名称来限定枚举器。
新的枚举之所以是“enum class
”,是因为它们结合了传统枚举的方面(命名值)和类的方面(带作用域的成员和没有转换)。这与该特性最初在 C++/CLI 中设计时的名称相同,之后才被提议用于 ISO C++。
能够指定底层类型可以简化互操作性并保证枚举的大小。
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
它还允许枚举的前向声明。
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
底层类型必须是带符号或无符号整数类型之一;默认是 int
。
在标准库中,使用了枚举类:
- 用于映射系统特定错误代码:在 : enum class errc;
- 用于指针安全指示器:在 : enum class pointer_safety { relaxed, preferred, strict };
- 用于 I/O 流错误:在 : enum class io_errc { stream = 1 };
- 用于异步通信错误处理:在 : enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };
其中有几个定义了诸如 ==
等运算符。
另请参见
- C++ 草案第 7.2 节
- [N1513=03-0096] David E. Miller: 改进枚举类型 (原始枚举提案)。
- [N2347 = J16/07-0207] David E. Miller, Herb Sutter, 和 Bjarne Stroustrup: 强类型枚举 (修订版 3)。
- [N2499=08-0009] Alberto Ganesh Barbati: 枚举的前向声明。
long long
– 更长的整数
至少 64 位长的整数。例如:
long long x = 9223372036854775807LL;
不,没有 long long long
,long
也不能写成 short long long
。
另请参见
- [05-0071==N1811] J. Stephen Adamczyk: 将
long long
类型添加到 C++ (修订版 3)。
扩展整数类型
如果存在扩展(精度)整数类型,则有一套关于其行为的规则。
参见
- [06-0058==N1988] J. Stephen Adamczyk: 将扩展整数类型添加到 C++ (修订版 1)。
广义联合体 (Generalized union
s)
在 C++98 中(与早期的非标准 C++ 版本一样),具有用户定义构造函数、析构函数或赋值的成员不能是 union
的成员。
union U {
int m1;
complex<double> m2; // error (silly): complex has constructor
string m3; // error (not silly): string has a serious invariant
// maintained by ctor, copy, and dtor
};
特别是:
U u; // which constructor, if any?
u.m1 = 1; // assign to int member
string s = u.m3; // disaster: read from string member
显然,先写入一个成员然后读取另一个成员是非法的,但人们仍然会这样做(通常是错误)。
C++11 修改了联合体的限制,以使更多成员类型可行;特别是,它允许带有构造函数和析构函数类型的成员。它还增加了一个限制,通过鼓励构建判别联合体,使更灵活的联合体不易出错。
联合体成员类型受到限制:
- 无虚函数(一如既往)
- 无引用(一如既往)
- 无基类(一如既往)
- 如果联合体有一个带有用户定义构造函数、拷贝构造函数或析构函数的成员,那么该特殊函数将被删除;也就是说,它不能用于联合体类型的对象。这是新的。
例如
union U1 {
int m1;
complex<double> m2; // ok
};
union U2 {
int m1;
string m3; // ok
};
这可能看起来容易出错,但新的限制有所帮助。特别是:
U1 u; // ok
u.m2 = {1,2}; // ok: assign to the complex member
U2 u2; // error: the string destructor caused the U destructor to be deleted
U2 u3 = u2; // error: the string copy constructor caused the U copy constructor to be deleted
基本上,除非您将 U2
嵌入到跟踪使用了哪个成员(变体)的结构中,否则 U2
将毫无用处。因此,构建判别联合体,例如:
class Widget { // Three alternative implementations represented as a union
private:
enum class Tag { point, number, text } type; // discriminant
union { // representation
point p; // point has constructor
int i;
string s; // string has default constructor, copy operations, and destructor
};
// ...
widget& operator=(const widget& w) // necessary because of the string variant
{
if (type==Tag::text && w.type==Tag::text) {
s = w.s; // usual string assignment
return *this;
}
if (type==Tag::text) s.~string(); // destroy (explicitly!)
switch (w.type) {
case Tag::point: p = w.p; break; // normal copy
case Tag::number: i = w.i; break;
case Tag::text: new(&s)(w.s); break; // placement new
}
type = w.type;
return *this;
}
};
另请参见
- C++ 草案第 9.5 节
- [N2544=08-0054] Alan Talbot, Lois Goldthwaite, Lawrence Crowl, 和 Jens Maurer: 无限制联合体 (修订版 2)
广义 POD
POD(“Plain Old Data”,普通旧数据)是指可以像 C 结构体一样操作的东西,例如可以使用 memcpy()
进行按位拷贝,可以使用 memset()
进行按位初始化等。在 C++98 中,POD 的实际定义基于对结构体定义中使用的语言特性的一组限制。
// S is a POD
struct S {
int a;
};
// SS was not a POD in C++98, is a POD in C++11
struct SS {
int a;
SS(int aa) : a(abs(aa)) { assert(a>=0); }
};
// Definitely not POD
struct SSS {
virtual void f(); /* ... */
};
在 C++11 中,S
和 SS
是“标准布局类型”(“POD 类型”的超集),因为 SS
实际上没有什么“魔力”:构造函数不影响布局(所以 memcpy()
会很好),只有初始化规则会影响(memset()
会很糟糕——不强制执行类型可能具有的任何不变式,例如 a >= 0
)。然而,SSS
仍然会有一个嵌入的 vptr,并且不会像“普通旧数据”那样。C++11 定义了 POD 类型、可平凡拷贝类型、平凡类型 和 标准布局类型 来处理以前是 POD 的各种技术方面。POD 是递归定义的:
- 如果所有成员和基类都是 POD,那么您就是 POD。
- 照常(详情见第 9 [10] 节)
- 无虚函数
- 无虚基类
- 无引用
- 无多个访问说明符
C++11 POD 最重要的方面是添加或删除构造函数不会影响布局或性能。
另请参见
- C++ 草案第 3.9 和 9 [10] 节
- [N2294=07-0154] Beman Dawes: POD 的再探讨;解决核心问题 568 (修订版 4)。