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;
}
輸出
訪問變量時,若型別不匹配會拋出異常,這樣導致我們在訪問變量時,要先去判斷當前變量的類型,還是有點麻煩。 特別我們只是要取出當前的數值時,因此引出下面的作法。
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;
}
輸出
值得一提的是,若是沒有實作相關 visitor function,且程式中又使用到。 compile 會直接報錯。
使用時機
替代傳統聯合(union): 傳統的 C++ 聯合無法安全地持有非平凡型別(如有析構函數的型別),並且需要手動管理型別,容易出錯。而 std::variant 提供了類型安全性和自動管理析構。
處理多種輸入類型: 如果一個變量需要在運行時動態地持有不同型別的值,std::variant 是一個很好的選擇。例如,你可以用它來表示一個多態的返回值,或是處理多種類型的輸入數據。
若你的情況符合上述情形,可以使用 std::variant 看看。