std::variant & std::visit
关于 C++17 中新增的 variant 和 visit 的内容总结
其他信息
[相关文档](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0088r3.html
)
上述链接中介绍了从 C++17 开始新增的 Variant 及其添加原因。简而言之,由于 Boost 库中相关功能已被使用太久,因此有人提议不要再拖延,应尽快将其纳入标准,将其作为 std::optional 引入。
内部结构
1. Variant
-
数据缓冲区 用于存储模板参数中最大类型所需的足够大内存空间,通常通过 Union 实现
-
标识符 用于表示当前数据缓冲区中存储了何种类型的对象的索引或标识符。通过使用该标识符,std::variant 能够保证类型安全性
凭借充足的内存空间,variant 类型能够实现如下行为。
struct Tracer
{
std::string name;
Tracer(const std::string& n) : name(n)
{
std::cout << " [+] '" << name << "' Tracer created (Constructor)\n";
}
// Copy Constructor
Tracer(const Tracer& other) : name(other.name)
{
std::cout << " [*] '" << name << "' Tracer copy-constructed (Copy Constructor)\n";
}
~Tracer()
{
std::cout << " [-] '" << name << "' Tracer destroyed (Destructor)\n";
}
};
// 1. Initialize the variant with a Tracer type.
std::cout << "1. Initializing variant with Tracer(\"Apple\").\n";
std::variant<int, Tracer> var = Tracer("Apple");
std::cout << " Variant now holds Tracer(\"Apple\").\n\n";
// 2. Assign a value of a different type (int).
std::cout << "2. Assigning integer 100 to the variant.\n";
var = 100;
std::cout << " Variant now holds integer 100.\n\n";
// 3. Assign another Tracer type value again.
std::cout << "3. Assigning Tracer(\"Banana\") to the variant.\n";
var = Tracer("Banana");
std::cout << " Variant now holds Tracer(\"Banana\").\n\n";
std::cout << "4. main function is about to end.\n";
return 0;
执行此类代码将得到如下结果。
1. Initializing variant with Tracer("Apple").
[+] 'Apple' Tracer created (Constructor)
[*] 'Apple' Tracer copy-constructed (Copy Constructor)
[-] 'Apple' Tracer destroyed (Destructor)
Variant now holds Tracer("Apple").
2. Assigning integer 100 to the variant.
[-] 'Apple' Tracer destroyed (Destructor)
Variant now holds integer 100.
3. Assigning Tracer("Banana") to the variant.
[+] 'Banana' Tracer created (Constructor)
[*] 'Banana' Tracer copy-constructed (Copy Constructor)
[-] 'Banana' Tracer destroyed (Destructor)
Variant now holds Tracer("Banana").
4. main function is about to end.
[-] 'Banana' Tracer destroyed (Destructor)
可以观察到,先调用原有值的析构函数,随后在栈上创建临时对象,最后将复制生成的值移入自身内存缓冲区。
因此,如果内部通过联合体(union)保留存储最大尺寸类的空间,无论variant类型中传入什么,都能确保其正常使用!
2. 访问机制
虽然不同库的实现各异,
-
函数指针表
-
Switch 语句
但通常可分为这两种形式。实际上,除了实现方式、开销以及编译器优化方面存在些许差异外,基本都可以理解为:通过检查标识符,根据激活的类型调用相应的函数。
댓글 작성
게시글에 대한 의견을 남겨 주세요.