范围enum(Scoped enum)
枚举使我们可以将一组整型常量组合在一起。与类一样,每个枚举定义一个新类型。枚举是文本类型。
C++有两种枚举:范围和非范围。新标准引入了范围枚举。我们使用关键字enum class
(或等价的enum struct
)定义一个范围枚举,后面跟着枚举名称和用大括号括起来的以逗号分隔的枚举器列表。分号跟在大括号后面:
Code:
enum class open_modes {input, output, append};
这里我们定义了一个名为open_modes
的枚举类型,它有三个枚举器:input
,output
和append
。
我们通过省略class
(或struct
)关键字来定义一个不带范围的枚举。枚举名称在非范围枚举中是可选的:
Code:
enum color {red, yellow, green}; // unscoped enumeration
// unnamed, unscoped enum
enum {floatPrec = 6, doublePrec = 10, double_doublePrec = 10};
如果枚举未命名,则只能将该类型的对象定义为枚举定义的一部分。与类定义一样,我们可以在结束枚举定义的大括号和分号之间提供一个用逗号分隔的声明符列表。
指定用于保存enum的类型
尽管每个枚举定义了一个唯一的类型,但它由一个内置的整型表示。在新标准下,我们可以通过在枚举名称后面加上冒号和要使用的类型的名称来指定该类型。
Code:
enum intValues : unsigned long long {
charTyp = 255, shortTyp = 65535, intTyp = 65535,
longTyp = 4294967295UL,
long_longTyp = 18446744073709551615ULL
};
如果不指定基础类型,那么默认情况下,范围枚举的基础类型为int
。非范围枚举没有默认值;我们只知道基础类型足够大,可以容纳枚举器值。当指定了基础类型(包括为范围枚举隐式指定)时,枚举器的值太大,不适合该类型是错误的。
由于能够指定枚举的基础类型,我们可以控制不同实现中使用的类型。我们可以确信,在一个实现下编译的程序在另一个实现上编译时会生成相同的代码。
enum的前向声明
根据新标准,我们可以前向声明enum
。enum
前向声明必须(隐式或显式地)指定枚举的基础大小:
Code:
// forward declaration of unscoped enum named intValues
enum intValues : unsigned long long; // unscoped, must specify a type
enum class open_modes; // scoped enums can use int by default
因为非范围枚举没有默认大小,所以每个声明都必须包含该枚举的大小。我们可以声明一个范围枚举,而不指定大小,在这种情况下,大小被隐式定义为int
。
与任何声明一样,给定枚举的所有声明和定义必须相互匹配。对于枚举,此要求意味着枚举的大小必须在所有声明和枚举定义中相同。
此外,我们不能在一个上下文中将名称声明为非范围枚举,并在以后将其重新声明为范围枚举:
Code:
// error: declarations and definition must agree whether the enum is scoped or unscoped
enum class intValues;
enum intValues; // error: intValues previously declared as scoped enum
enum intValues : long; // error: intValues previously declared as int
标准库mem_fn类模板
要使用function
,我们必须提供要调用的成员的调用签名。相反,我们可以使用另一个库工具mem_fn
让编译器来推断成员的类型,该工具在头文件
中定义。与function
一样,mem_fn
从指向成员的指针生成可调用对象。与function
不同,mem_fn
将从指向成员的指针类型推导出可调用类型:
Code:
find_if(svec.begin(), svec.end(), mem_fn(&string::empty));
这里我们使用mem_fn(&string::empty)
生成一个可调用对象,该对象接受一个字符串参数并返回一个bool
。
mem_fn
生成的可调用对象可以在对象或指针上调用:
Code:
auto f = mem_fn(&string::empty); // f takes a string or a string*
f(*svec.begin()); // ok: passes a string object; f uses .* to call empty
f(&svec[0]); // ok: passes a pointer to string; f uses .-> to call empty
实际上,我们可以将mem_fn
想象成它使用重载的函数调用操作符生成一个可调用的函数,一个接受string*
,另一个接受string&
。
参考文献
[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.