--- 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); } ```