131 lines
3.1 KiB
Markdown
131 lines
3.1 KiB
Markdown
---
|
||
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);
|
||
}
|
||
```
|