Skip to content

Latest commit

 

History

History
62 lines (47 loc) · 4.1 KB

File metadata and controls

62 lines (47 loc) · 4.1 KB

第十六章 std::variant<>

std::variant<>相当于C++标准库提供的一种新的union类,诸多好处之一是提供一种新的多态工具来处理非同构集合(inhomogeneous collection)。也就是说,它允许我们处理包含不同类型的容器,并且不需要创建一个公共的基类或者指针(原生指针或者智能指针)。

16.1 提供std::variant<>的动机

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也支持移动语义。

16.2 使用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都是不允许的。

15.4 后记

可空对象首先由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。