cpp11 语言类型

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

其中有几个定义了诸如 == 等运算符。

另请参见

long long – 更长的整数

至少 64 位长的整数。例如:

    long long x = 9223372036854775807LL;

不,没有 long long longlong 也不能写成 short long long

另请参见

扩展整数类型

如果存在扩展(精度)整数类型,则有一套关于其行为的规则。

参见

广义联合体 (Generalized unions)

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

另请参见

广义 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 中,SSS 是“标准布局类型”(“POD 类型”的超集),因为 SS 实际上没有什么“魔力”:构造函数不影响布局(所以 memcpy() 会很好),只有初始化规则会影响(memset() 会很糟糕——不强制执行类型可能具有的任何不变式,例如 a >= 0)。然而,SSS 仍然会有一个嵌入的 vptr,并且不会像“普通旧数据”那样。C++11 定义了 POD 类型可平凡拷贝类型平凡类型标准布局类型 来处理以前是 POD 的各种技术方面。POD 是递归定义的:

  • 如果所有成员和基类都是 POD,那么您就是 POD。
  • 照常(详情见第 9 [10] 节)
    • 无虚函数
    • 无虚基类
    • 无引用
    • 无多个访问说明符

C++11 POD 最重要的方面是添加或删除构造函数不会影响布局或性能。

另请参见