摘要訊息 : C++ 11 之後就應該遺棄弱型別列舉嗎? 列舉還有哪一些不知名的特性?
0. 前言
在《C++ 學習筆記》中, 我們已經詳細講述過了列舉, 這篇文章是針對《C++ 學習筆記》中的列舉進行補充. 如果閣下沒有學習過 C++ 中的列舉, 那麼建議先閱讀《C++ 學習筆記》.
更新紀錄 :
- 2022 年 5 月 16 日進行第一次更新和修正.
1. 再論列舉
C++ 中的列舉分為弱型別和強型別 :
- 強型別列舉 (
enum class
) : 要向數值型別進行型別轉化需要借助static_cast
; - 弱型別列舉 (
enum
) : 可以隱含地向數值型別進行轉化, 包括浮點數型別.
在前置宣告的方面, 強型別列舉和弱型別列舉的表現不相同. 強型別列舉支援提前宣告, 但是弱型別列舉不直接支援提前宣告. 在明確標識列舉值的型別之後, 弱型別列舉也可以提前宣告.
C++ 11 引入強型別列舉之後, 是否意味著弱型別列舉應該完全被淘汰呢? 答案顯然是否定的. 根據哲學的說法, 萬物存在皆有理. 首先, 弱型別列舉是從 C 語言遺傳的, 如果取消弱型別列舉, 那麼會導致 C++ 和 C 的直接不相容. 下面再舉一個實例, 說明弱型別列舉在 C++ 11 之後仍然有用 :
#include <tuple>
int main(int argc, char *argv[]) {
tuple<string /* name */, long /* number */, char /* age */> t {"Jonny", 1, 18};
enum user_information {
name, number, age
};
auto name = std::get<name>(t); //獲取名稱, 等價於 std::get<0>(t)
// ...
}
可以看到, 列舉使得 std::tuple
中存儲的信息更加清晰. 但是如果使用強型別列舉, 那麼我們就需要寫成 auto name = std::get<static_cast<int>(user_information::name)>(t);
, 這顯然過於複雜.
另外, 在函式樣板中, 如果強型別列舉作為樣板引數, 它的表現可能不如弱型別列舉要好 :
template <typename E>
void f() {
E e;
//...
type a = static_cast<...>(E::some_value); // 向本來應有的型別進行轉型, 但是我們不知道列舉值本來持有什麼樣的型別
}
在 Code 2 中, 如果 E
是弱型別列舉, 那麼我們可以直接把 type
替換為我們想要的型別; 但是如果 E
是強型別列舉, 那麼這個 type
我們沒有辦法去猜測. 這個時候, 我們需要借助來自標頭檔 <type_traits>
的 std::underlying_tpye
來萃取列舉值原本持有的型別 :
#include <type_traits>
template <typename E>
void f() {
E e;
//...
auto a = static_cast<typename std::underlying_type<E>::type>(E::some_value);
}
2. using
與列舉
在《C++ 學習筆記》中, 我們沒有提到 using
和列舉的配合用法. 實際上, using
可以提升單個或者多個列舉值的可視範圍 :
#include <iostream>
enum E {
x = 2020, y, z
};
namespace NS {
using ::E::x, ::E::y;
}
int main(int argc, char *argv[]) {
std::cout << NS::x + NS::y << std::endl; // OK, 輸出 : 2020
}
當然, C++ 20 還引入了直接使用 using enum
來提升列舉值的可視範圍, 這個新特性可以閱讀《【C++ 20】Using Enum》來了解.
自創文章, 原著 : Jonny. 如若閣下需要轉發, 在已經授權的情況下請註明本文出處 :