Files
hugo_blog.awin.one/content/posts/2019/2019-12-22_[Cpp 筆記] 好用的 optional/index.md
2025-03-09 23:28:35 +08:00

3.1 KiB
Raw Blame History

slug, title, description, toc, authors, tags, categories, series, date, lastmod, featuredVideo, featuredImage, draft, enableComment
slug title description toc authors tags categories series date lastmod featuredVideo featuredImage draft enableComment
[C++ 筆記] 好用的 std::optional [C++ 筆記] 好用的 std::optional true
awin
c++
Programming
C++ 筆記
2019-12-22T00:00:00 2019-12-22T00:00:00 false true

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