std::variant<>
相当于C++标准库提供的一种新的union类,诸多好处之一是提供一种新的多态工具来处理非同构集合(inhomogeneous collection)。也就是说,它允许我们处理包含不同类型的容器,并且不需要创建一个公共的基类或者指针(原生指针或者智能指针)。
C++吸收了C中很多特性,其中就包括union,这种类型的对象持有一系列类型中的一个。然而,这个语言特性有些缺点:
- 对象不知道到底当前它持有哪种类型
- 出于上述原因,它不允许有非平凡成员数据成员(non-trivial member),比如
std::string
- 你不能继承自一个union
但是自从C++提供std::variant<>
后,我们现在就有了一个闭合且可区分的union(这意味着它有一些可能的类型,并且你可以指定究竟是哪种类型)(译注:这里 闭合且可区分union 其实意思就是 类型数量固定且可以区分开来的union),现在
- 当前值的类型总是可知的
- 你可以有任何类型的数据成员
- 你也可以继承自它
事实上,std::variant<>
持有众多可选值中的一个,这些可选值通常有不同的类型,但是两个可选值也可以同类型,这在某些情况下是有用的,比如相同类型的可选值代表不同的语义(举个例子,两个字符串可选值,表示不同的数据库中的列,所以即使同类型你也直到到底这个类型是表示哪个类)。
variant
内部的内存构造可以简单的表示为底层类型中占内存最大的一种加上一些固定开销,这些固定开销就是用来管理可选值的数据结构。std::variant
不会产生堆内存分配。
通常,std::variant
不能是空的,除非你刻意用某个可选值来代表空值。然而,在一些极端的情况下(比如赋予一个不同类型的新值引发异常),std::variant
会进入一种空值状态。
与std::optional<>
、std::any
类似,std::variant
也具有值语义。拷贝一个std::variant
会创建一个独立的对象,然后将原对象可选值中的当前值深拷贝至新对象。因此,拷贝std::variant
是否高开销完全取决于拷贝当前值。另外std::variant
也支持移动语义。
下面的代码演示了std::variant<>
的核心能力:
#include <variant>
#include <iostream>
int main()
{
std::variant<int, std::string> var{"hi"}; // initialized with string alternative
std::cout << var.index() << '\n'; // prints 1
var = 42; // now holds int alternative
std::cout << var.index() << '\n'; // prints 0
...
try {
int i = std::get<0>(var); // access by index
std::string s = std::get<std::string>(var); // access by type (throws exception in this case)
...
}
catch (const std::bad_variant_access& e) { // in case a wrong type/index is used
std::cerr << "EXCEPTION: " << e.what() << '\n';
...
}
}
初始化和赋值操作会使用最佳匹配来寻找可选值。如果类型不完全匹配,结果可能让人惊讶。
注意,空的std::variant
、带引用成员的std::variant
、带C风格数组的std::variant
或者带不完整类型(incomplete type,比如void)的std::variant
都是不允许的。
可空对象首先由Fernando Cacciola在2005的https://wg21.link/n1878中提出。Fernando Cacciola和Andrzej Krzemienski提出新提案https://wg21.link/n3793被Library Fundamentals TS接受
Beman Dawes和Alisdair Meredith的新提案https://wg21.link/p0220r1被其他C++17组件接受。
Tony van Eerd极大的改进了比较操作的语义,提案参见[https:// wg21.link/n3765](https:// wg21.link/n3765)和https://wg21.link/p0307r2。 Vicente J. Botet Escriba 优化了std::optional<> 、std::variant<>和std::anyAPI,提案参见https://wg21.link/p0032r3。Jonathan Wakely修复了in_place的行为,提案参见https://wg21.link/p0504r0。