[C++ 筆記] 好用的 std::optional
since C++17
std::optional
讓 function 的回傳值多了一個選擇:有值或是 nullopt
。
例如我們有一個 function 要從某個檔案讀值出來(或是去 DB 查一個東西之類的需求),就假設我們要讀一個 int,我們也許會這樣寫:
int readData(std::string filePath) {
...
int data = readFromFile(...);
...
return data;
}
但如果要讀的檔案不存在呢?這時候要 return 什麼呢?所以這時候我們會改成這樣寫:
bool readData(std::string filePath, int& readData) {
FILE* file = fopen(...)
if (!file) {
return false;
}
readData = readFromFile(...);
return true;
}
我們用一個回傳值代表檔案讀取失敗與否,要是檔案存在且開啟成功,那麼 readData
這個回傳值就代表我們讀到的值。要是回傳值是 false
,那 readData
就沒有意義。
但是這樣會讓傳入跟傳出的參數混在一起,第一時間也不容易直覺的分辨出來,像這種情況 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
可以達到這個目的:
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()
以外,還有其他的取值方法:
std::optional<std::string> name;
// Some process...
if (name)
{
printf("name = %s\n", (*name).c_str());
}
如果 std::optional
包含的是 struct 或是 class,也可以用 ->
來直接存取 member(或 member function):
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);
}