This commit is contained in:
2025-03-09 23:28:35 +08:00
parent c6983b2ba4
commit 437a8834ce
6 changed files with 287 additions and 0 deletions

View File

@@ -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`
<!--more-->
例如我們有一個 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<int> 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<T>::value()`](https://en.cppreference.com/w/cpp/utility/optional/value) 以外,還有其他的取值方法:
```cpp
std::optional<std::string> 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> 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);
}
```