From e2665a4b17858990294eb79fbb42e9c554215e60 Mon Sep 17 00:00:00 2001 From: Awin Huang Date: Sat, 8 Apr 2023 21:20:58 +0800 Subject: [PATCH] =?UTF-8?q?Add=20"[C++=20=E7=AD=86=E8=A8=98]=20=E5=A5=BD?= =?UTF-8?q?=E7=94=A8=E7=9A=84=20optional"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2019/[C++ 筆記] 好用的 optional/index.md | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 content/posts/2019/[C++ 筆記] 好用的 optional/index.md diff --git a/content/posts/2019/[C++ 筆記] 好用的 optional/index.md b/content/posts/2019/[C++ 筆記] 好用的 optional/index.md new file mode 100644 index 0000000..28a3456 --- /dev/null +++ b/content/posts/2019/[C++ 筆記] 好用的 optional/index.md @@ -0,0 +1,130 @@ +--- +slug: "[C++ 筆記] 好用的 std::optional" +title: "[C++ 筆記] 好用的 std::optional" +description: +toc: true +authors: + - awin +tags: + - c++ +categories: + - Programming +series: + - C++ 筆記 +date: 2019-12-22T00:00:00 +lastmod: 2019-12-22T00:00:00 +featuredVideo: +featuredImage: +draft: false +enableComment: true +--- + +> since C++17 + +[`std::optional`](https://en.cppreference.com/w/cpp/utility/optional) 讓 function 的回傳值多了一個選擇:**有值**或是 `nullopt`。 + + + +例如我們有一個 function 要從某個檔案讀值出來(或是去 DB 查一個東西之類的需求),就假設我們要讀一個 int,我們也許會這樣寫: +```cpp +int readData(std::string filePath) { + ... + int data = readFromFile(...); + ... + + return data; +} +``` + +但如果要讀的檔案不存在呢?這時候要 return 什麼呢?所以這時候我們會改成這樣寫: +```cpp +bool readData(std::string filePath, int& readData) { + FILE* file = fopen(...) + if (!file) { + return false; + } + + readData = readFromFile(...); + + return true; +} +``` + +我們用一個回傳值代表檔案讀取失敗與否,要是檔案存在且開啟成功,那麼 `readData` 這個回傳值就代表我們讀到的值。要是回傳值是 `false`,那 `readData` 就沒有意義。 +但是這樣會讓傳入跟傳出的參數混在一起,第一時間也不容易直覺的分辨出來,像這種情況 Python 就清楚很多: +```python +def readData(filePath): + data = None + try: + with open(filePath, "rb") as f: + data = f.read(...) + return data + except FileNotFoundError as e: + return None + +value = readData("123.txt") +if value: + pass # Do something... +``` +可以像 Python 那樣都從回傳值來判斷嗎? `std::optional` 可以達到這個目的: +```cpp +std::optional readData(std::string filePath) { + FILE* file = nullptr; + fopen_s(&file, filePath.c_str(), "rb"); + if (!file) { + return std::nullopt; + } + + int readData; + fread(&readData, 1, sizeof(readData), file); + fclose(file) + + return readData; +} + +auto result = readData("123.txt"); +if (result) { + auto value = result.value(); + // Use value here... +} + +// 或是這樣寫也可以 +if (result == std::nullopt) { + // Error handle here +} else { + auto value = result.value(); + // Use value here... +} +``` + +雖然用起來沒辦法讓 C++ 像 Python 那麼簡單,但是 `std::optional` 確實讓整段 code 看起來更清楚了。 +除了 [`std::optional::value()`](https://en.cppreference.com/w/cpp/utility/optional/value) 以外,還有其他的取值方法: +```cpp +std::optional name; + +// Some process... + +if (name) +{ + printf("name = %s\n", (*name).c_str()); +} +``` + +如果 `std::optional` 包含的是 struct 或是 class,也可以用 `->` 來直接存取 member(或 member function): +```cpp +struct User { + uint32_t id; + std::string name; + int32_t age; +}; + +std::optional user; + +// Some process... + +if (user) { + printf("id = %d\n", user->id); + printf("name = %s\n", user->name.c_str()); + printf("age = %d\n", user->age); +} +```