Home std::variant (中文)
Post
Cancel

std::variant (中文)

Variant

std::variant 是 C++17 引入的類型安全的聯合(union),允許變量在運行時持有多種不同的型別之一。這類似於 Java 或 TypeScript 中的聯合型別,但在 C++ 中提供了編譯期的型別安全性。

講 variant 之前,必須先提一下 union, 因為 union 這個東西實在太特別了。

傳統聯合(union)是 C++ 和 C 語言中的一種數據結構,它允許在同一內存空間中儲存不同型別的數據。也就是說,一個 union 變量可以在不同時間儲存不同型別的數據,但每次只能儲存其中一種型別。它的內存大小等於其成員中最大的那個型別的大小。

啥時要用 union ?

適用於一些內存敏感的場景,特別是在嵌入式系統或需要手動優化內存使用的低層系統編程中。它通常用於代表多種可能的數據型別,但這些型別不會同時存在的情況。

像是測量值可能以不同的單位表示時,如米(meters)、公里(kilometers)、或英里(miles)。這些單位是互斥的,因為在特定時間我們只會以一種單位來表示該測量值。

Variant Codes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <variant>
#include <string>

int main() {
    // 定義一個 std::variant,它可以持有 int、float 或 std::string
    std::variant<int, float, std::string> var;

    // 將不同型別的值賦予變量
    var = 81;  // int
    std::cout << "int: " << std::get<int>(var) << std::endl;

    var = 3.3333f;  // float
    std::cout << "float: " << std::get<float>(var) << std::endl;

    var = "Hello, variant!";  // std::string
    std::cout << "string: " << std::get<std::string>(var) << std::endl;

    // 檢查當前變量持有的型別
    if (std::holds_alternative<std::string>(var)) {
        std::cout << "The variant holds a string." << std::endl;
    }

    // 訪問變量時,若型別不匹配會拋出異常
    try {
        int value = std::get<int>(var);  // 錯誤,當前 var 持有的是 std::string
    }
    catch (const std::bad_variant_access& e) {
        std::cout << "Exception: " << e.what() << std::endl;
    }

    return 0;
}

輸出

Desktop View

訪問變量時,若型別不匹配會拋出異常,這樣導致我們在訪問變量時,要先去判斷當前變量的類型,還是有點麻煩。 特別我們只是要取出當前的數值時,因此引出下面的作法。

variant 模式匹配

模式匹配: std::variant 配合 std::visit 來進行模式匹配,使得訪問不同型別時更具表現力和安全性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <variant>

struct Visitor {
    void operator()(int i) const {
        std::cout << "int: " << i << std::endl;
    }
    void operator()(float f) const {
        std::cout << "float: " << f << std::endl;
    }
    void operator()(const std::string& s) const {
        std::cout << "string: " << s << std::endl;
    }
};

int main() {
    std::variant<int, float, std::string> var = "Hello, variant!";

    std::visit(Visitor{}, var);  // 使用訪問者模式訪問變量

    return 0;
}

輸出

Desktop View

值得一提的是,若是沒有實作相關 visitor function,且程式中又使用到。 compile 會直接報錯。

Desktop View

Desktop View

使用時機

  1. 替代傳統聯合(union): 傳統的 C++ 聯合無法安全地持有非平凡型別(如有析構函數的型別),並且需要手動管理型別,容易出錯。而 std::variant 提供了類型安全性和自動管理析構。

  2. 處理多種輸入類型: 如果一個變量需要在運行時動態地持有不同型別的值,std::variant 是一個很好的選擇。例如,你可以用它來表示一個多態的返回值,或是處理多種類型的輸入數據。

若你的情況符合上述情形,可以使用 std::variant 看看。

☝ツ☝

This post is licensed under CC BY 4.0 by the author.

👈 ツ 👍