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