强类型枚举和非强类型枚举

  • C++11优化

    • C++11

      • C++11支持枚举有类型. 避免命名污染。

      • C++11支持枚举继承类型. 不被编译器主导类型,从而类型不定.

      • C++11支持枚举声明. 不用因为修改而导致没有用到的一些函数需要编译.

    • C++98

      • 哪儿声明,在哪儿用. 局部有效. 污染命名空间.

      • 不能声明,只能定义.

  • 污染命名空间

    • 分析

      • C++98enum哪儿定义,哪儿使用.
      • 而且没有限制,定义了就相当于是一个变量.
    • 案例

      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的形式下,仍然可以很好的工作.同时兼顾不污染命名空间,以及良好的工作.

  • 發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章