C++11
优化
C++11
C++11
支持枚举有类型. 避免命名污染。
C++11
支持枚举继承类型. 不被编译器主导类型,从而类型不定.
C++11
支持枚举声明. 不用因为修改而导致没有用到的一些函数需要编译.
C++98
哪儿声明,在哪儿用. 局部有效. 污染命名空间.
不能声明,只能定义.
污染命名空间
分析
C++98
的enum
哪儿定义,哪儿使用.- 而且没有限制,定义了就相当于是一个变量.
案例
int main() { enum Color { red, black, white }; int red = 1; return 0; }
编译错误. 造成了小概率的命名污染.
说明
这种泄露的枚举有个学名叫做
unscoped
. 非限制枚举.
C++11
int main() { enum class Color { red, black, white }; int red = 1; return 0; }
编译执行正常通过.是一个类型.
有界限枚举.
强类型
回顾
仅仅不污染命名这一个理由就可以让开发者拥抱
C++11
了.除此之外,
C++11
支持强类型.非强类型的
Enum
大部分编译器可以隐式的将非限制型枚举转为
int
,后来还支持float
.非强类型的隐式转换
int main() { enum Color { red, black, white }; int redv = red >= 0 ? red : -1; return 0; }
支持了隐式转换, 类型不明确.
限制型类型
int main() { enum class Color { red, black, white }; int redv = red >= 0 ? red : -1; return 0; }
编译报错,不支持这类隐式转换.
限制型枚举保留能力
int main() { enum class Color { red, black, white }; Color c = Color::red; int redv = static_cast<int>(c) >= 0 ? 0 : -1; return 0; }
规避了隐式转换,也保留了转换的机制.
声明
回顾
前面提及了两种
C++11
的优点.规避命名空间污染,强类型避免隐式转换.
现在介绍一个声明和定义分离的好处.
c++98
不支持声明.
C++11
enum Color; enum Color {red,black}; int main() { Color c; }
非限制类型也不支持.
限制强类型.
enum class Color; enum class Color {red,black}; int main() { Color c; }
支持强类型.
强类型和编译
编译器对于枚举,根据编译选项和编译规则和编译策略,会给不同的类型.
比如枚举范围很小,就用
char
,大一点就用short
,再大一点就用int
,longlong
之类的.有的可能为了效率,对齐之类的,将枚举定义为
int,longlong
也是可能的.
c++98
仅仅支持简单的定义,声明不支持,强类型也不支持.
c++98
哪儿用哪儿定义,哪儿用哪儿确定类型.
这种可能会增加编译负担.
比如这个头文件都包含了,原则是不修改的,但是就是修改了,可能整个项目都包含了这个头文件.
因为这一个修改导致整个就修改了。
可以提前声明就会好很多.定义则在真正用于实现的
.cpp
使用的时候才include
.强类型的好处
支持声明和定义的拆分.头文件声明.
cpp
中才真正包含头文件.不会因为修改太多而导致不使用的地方也被编译.
声明并确定类型
#include <iostream> enum class Color:char; enum class Color:char {red,black}; int main() { Color c; std::cout << sizeof(c) << std::endl; }
这种提前声明并知晓大小. 就可以明确的编译.
非限制的好处
前言
想不到也有用武之地,就是在
tuple
的场景.或者说是根据
key
获取对应的value
.数字
#include <iostream> #include <string> #include <tuple> using UserInfo = // type alias; see Item 9 std::tuple<std::string, // name std::string, // email std::size_t>; // reputation int main() { UserInfo info("hello","word",1); std::cout << std::get<0>(info) << std::endl; std::cout << std::get<1>(info) << std::endl; std::cout << std::get<2>(info) << std::endl; }
用数字,不够灵活.需要跟着变动.
枚举
#include <iostream> #include <string> #include <tuple> enum Key { name, email, reputation }; using UserInfo = // type alias; see Item 9 std::tuple<std::string, // name std::string, // email std::size_t>; // reputation int main() { UserInfo info("hello","word",1); std::cout << std::get<name>(info) << std::endl; std::cout << std::get<email>(info) << std::endl; std::cout << std::get<reputation>(info) << std::endl; }
将变化的部分提取出来了. 成为枚举.
就是没有规避命名空间污染.
C++11
#include <iostream> #include <string> #include <tuple> enum class Key:char { name, email, reputation }; template <typename T> constexpr T E2T(Key k) noexcept { return static_cast<T>(k); } using UserInfo = // type alias; see Item 9 std::tuple<std::string, // name std::string, // email std::size_t>; // reputation int main() { UserInfo info("hello","word",1); std::cout << std::get<E2T<std::size_t>(Key::name)>(info) << std::endl; std::cout << std::get<E2T<std::size_t>(Key::email)>(info) << std::endl; std::cout << std::get<E2T<std::size_t>(Key::reputation)>(info) << std::endl; }
看着是有点复杂,但是可以简化.
有个小问题是
Key
的类型可能不是size_t
.优化版本
#include <iostream> #include <type_traits> #include <string> #include <tuple> enum class Key { name, email, reputation }; template <typename T> constexpr typename std::underlying_type<T>::type E2T(T k) noexcept { return static_cast<typename std::underlying_type<T>::type>(k); } using UserInfo = // type alias; see Item 9 std::tuple<std::string, // name std::string, // email std::size_t>; // reputation int main() { UserInfo info("hello","word",1); std::cout << std::get<E2T(Key::name)>(info) << std::endl; std::cout << std::get<E2T(Key::email)>(info) << std::endl; std::cout << std::get<E2T(Key::reputation)>(info) << std::endl; }
简化版本,主要看模板的实现.
总结
限制枚举可以避免命名空间污染.
限制枚举可以避免莫名其妙的隐式转换.
限制枚举可以声明定义分离,减少头文件依赖其他头文件,提高编译效率.
限制枚举在
key:value
的形式下,仍然可以很好的工作.同时兼顾不污染命名空间,以及良好的工作.