強類型枚舉和非強類型枚舉

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

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