Add uv
This commit is contained in:
130
content/posts/2019/2019-12-22_[Cpp 筆記] 好用的 optional/index.md
Normal file
130
content/posts/2019/2019-12-22_[Cpp 筆記] 好用的 optional/index.md
Normal 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);
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user