|
本文經(jīng)授權(quán)轉(zhuǎn)自公眾號(hào)CSDN(ID:CSDNnews)
作者 | Bart?omiej Filipek
隨著 C++ 標(biāo)準(zhǔn)的不斷演進(jìn),枚舉類型(enum class)作為一種重要的數(shù)據(jù)結(jié)構(gòu),在 C++ 社區(qū)中扮演著越來越重要的角色。從 C++17 到 C++23,我們見證了枚舉類型的多項(xiàng)改進(jìn)和完善,這些變化不僅增強(qiáng)了語言本身的表達(dá)能力,也為開發(fā)者提供了更強(qiáng)大、更安全的編程工具。
原文鏈接:https://www.cppstories.com/2024/enum-improvements/
C++語言的演進(jìn)不斷帶來強(qiáng)大的新特性,提升了代碼的安全性、可讀性和可維護(hù)性。在這些改進(jìn)中,我們見證了從 C++17、C++20 到 C++23 中對(duì) enum class 功能的修改和擴(kuò)展。在這篇文章中,我們將探討這些進(jìn)展,重點(diǎn)介紹 C++17 中的初始化改進(jìn)、C++20 中引入的 using enum 關(guān)鍵字,以及 C++23 中的 std::to_underlying 實(shí)用工具。
1、enum class 簡介
在深入了解這些改進(jìn)之前,讓我們先簡要回顧一下 enum class 是什么。enum class(限定作用域的枚舉)提供了一種類型安全的方式來定義一組命名常量。與傳統(tǒng)的(無作用域)枚舉不同,enum class 不會(huì)隱式轉(zhuǎn)換為整數(shù)或其他類型,從而防止了意外的誤用。下面是一個(gè)基本示例:
#include
enum class Color { Red, Green, Blue};
int main() { Color color = Color::Red;
if (color == Color::Red) std::cout "The color is red.
";
color = Color::Blue;
if (color == Color::Blue) std::cout "The color is blue.
";
// std::cout // int i = color; // error: cannot convert}注意,在 main 函數(shù)的末尾有兩行代碼。由于沒有隱式轉(zhuǎn)換為整數(shù)類型,因此會(huì)出現(xiàn)編譯器錯(cuò)誤。
作為對(duì)比,下面是一個(gè)類似的例子,但使用的是無作用域枚舉:
#include
enum Color { Red, Green, Blue};
int main() { Color color = Red;
if (color == Red) std::cout "The color is red.
";
color = Blue;
if (color == Blue) std::cout "The color is blue.
";
std::cout // fine, prints integer value! int i = color; // fine, can convert...}簡而言之,enum class 為所有枚舉值提供了單獨(dú)的作用域,同時(shí)也加強(qiáng)了類型安全。沒有隱式的整數(shù)轉(zhuǎn)換,這樣你就能更好地控制設(shè)計(jì)。
以上的基礎(chǔ)部分很簡單,接下來讓我們來看看最新 C++ 版本中的一些實(shí)用改進(jìn)。
2、C++17:使用大括號(hào)初始化基礎(chǔ)類型
有時(shí)候,enum class 可能顯得過于限制,某些情況下的轉(zhuǎn)換可能會(huì)很方便。
在 C++17 中,P0138 提案被接受,以下是其中的一個(gè)示例:
enum class Handle : uint32_t { Invalid = 0 }; Handle h { 42 }; // OK簡而言之,當(dāng)你使用 enum class 來定義強(qiáng)類型時(shí),允許從基礎(chǔ)類型進(jìn)行初始化而不產(chǎn)生任何錯(cuò)誤——這在 C++17 之前是無法實(shí)現(xiàn)的。
這個(gè)變化仍能確保枚舉仍然是安全的,因?yàn)樗鼈冎荒苡糜诮y(tǒng)一/大括號(hào)初始化。請(qǐng)看看下面的代碼:
#include
enum class Handle : uint32_t { Invalid = 0 };
void process(Handle h) {
}
int main() { Handle h { 42 }; // OK
// process({10}); // error process(Handle{10});}你不能直接將 {10} 作為 process 函數(shù)的參數(shù),仍需要明確指定類型。
在 C++14 中,你可以使用 process(static_cast(10));——如你所見,C++17 版本的改進(jìn)要好得多。
3、C++20:使用 using enum
C++20 引入了 using enum 語法,這個(gè)特性允許你將一個(gè)枚舉的所有枚舉值引入當(dāng)前作用域,同時(shí)不失去作用域枚舉的優(yōu)點(diǎn)。請(qǐng)看下面的示例:
enum class ComputeStatus { Ok, Error, FileError, NotEnoughMemory, TimeExceeded, Unknown};在早期的 C++ 版本中,使用這些枚舉值時(shí),需要使用枚舉類的名稱進(jìn)行限定:
ComputeStatus s = ComputeStatus::NotEnoughMemory;C++20 通過 using enum 聲明簡化了這一點(diǎn):
int main() { using enum ComputeStatus; ComputeStatus s = NotEnoughMemory;}上面的簡單代碼可能沒什么實(shí)際意義,但看看下面這個(gè)例子:
int main() { ComputeStatus s = ComputeStatus::Ok; switch (s) { case ComputeStatus::Ok: std::cout "ok"; break; case ComputeStatus::Error: std::cout "Error"; break; case ComputeStatus::FileError: std::cout "FileError"; break; case ComputeStatus::NotEnoughMemory: std::cout "NotEnoughMemory"; break; case ComputeStatus::TimeExceeded: std::cout "Time..."; break; default: std::cout "unknown..."; }}我們可以將其轉(zhuǎn)換為如下形式:
int main() { ComputeStatus s = ComputeStatus::Ok; switch (s) { using enum ComputeStatus; // case Ok: std::cout "ok"; break; case Error: std::cout "Error"; break; case FileError: std::cout "FileError"; break; case NotEnoughMemory: std::cout "NotEnoughMemory"; break; case TimeExceeded: std::cout "Time..."; break; default: std::cout "unknown..."; }}或者,也可以看看下面這個(gè)例子:
struct ComputeEngine { enum class ComputeStatus { Ok, Error, FileError, NotEnoughMemory, TimeExceeded, Unknown }; using enum ComputeStatus;};
int main() { ComputeEngine::ComputeStatus s = ComputeEngine::Ok;}你可以將所有枚舉值引入 ComputeEngine 的作用域中,同時(shí)享受 enum class 帶來的類型安全特性。
C++20 的這一改進(jìn)使代碼更加簡潔,并減少了冗余,尤其是在某個(gè)作用域內(nèi)頻繁使用多個(gè)枚舉值的情況下。它提供了一種更加流暢和可讀的方式,同時(shí)不犧牲作用域枚舉所提供的類型安全性。
4、C++23:std::to_underlying
C++23 通過引入 std::to_underlying 進(jìn)一步增強(qiáng)了 enum class 的可用性,這個(gè)實(shí)用函數(shù)可以將枚舉值轉(zhuǎn)換為其基礎(chǔ)的整型類型,這個(gè)特性解決了將枚舉值轉(zhuǎn)換為整數(shù)以用于存儲(chǔ)、比較或與其他期望整型的 API 交互的常見需求。
這個(gè)想法最早出現(xiàn)在 Scott Meyers 的經(jīng)典著作《Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14》一書中。終于在 C++23 中,我們可以享受到這個(gè)被標(biāo)準(zhǔn)化的功能。
在 C++23 之前,將枚舉轉(zhuǎn)換為其基礎(chǔ)類型需要顯式的類型轉(zhuǎn)換:
enum class Permissions : uint8_t { Execute = 1, Write = 2, Read = 4};
uint8_t value = static_castuint8_t>(Permissions::Read);而有了 std::to_underlying,這個(gè)轉(zhuǎn)換變得更加直接和清晰:
#include
int main() { Permissions p = Permissions::Read; auto value = std::to_underlying(p); // C++23}std::to_underlying 函數(shù)提高了代碼的可讀性,并減少了與類型轉(zhuǎn)換相關(guān)的樣板代碼。它還明確了意圖,使得人們能一目了然地知道這是在獲取枚舉的基礎(chǔ)值。
5、未來的改進(jìn)
接下來,一個(gè)即將到來的重要更新可能是支持 C++26 反射——可查看提案 P2996(雖然這個(gè)提案還未被接受,但預(yù)計(jì)很快就會(huì)獲得批準(zhǔn))。反射帶來了許多激動(dòng)人心的可能性,比如將枚舉轉(zhuǎn)換為字符串的能力。
請(qǐng)看提案中的這個(gè)例子:
template typename E> requires std::is_enum_vconstexpr std::string enum_to_string(E value) { template for (constexpr auto e : std::meta::enumerators_of(^E)) { if (value == [:e:]) { return std::string(std::meta::name_of(e)); } } return "";}
enum Color { red, green, blue };static_assert(enum_to_string(Color::red) == "red");static_assert(enum_to_string(Color(42)) == "");當(dāng)然,你也可以不必等到 C++26,依賴第三方庫就能體驗(yàn)這個(gè)功能,如 Neargye/magic_enum @GitHub。
本文轉(zhuǎn)自公眾號(hào)“CSDN”,ID:CSDNnews分享個(gè)群友推薦的招聘類小程序這里分享一個(gè)交流群群友推薦的招聘小程序,其中有很多二三線城市,比如赤峰、保定、阿克蘇之類的三四線城市,還支持按照崗位、地點(diǎn)和薪資要求來找合適的崗位。
經(jīng)常遇到一些想回老家或者二三線城市的同學(xué)苦于沒有合適的去處,打開boss直聘和獵聘網(wǎng)這些招聘類軟件,結(jié)果發(fā)現(xiàn)好的崗位基本都集中在一線城市,很少看到那種有二三線城市的招聘崗位。 |
|