Boost C++ 库

“...世界上最受尊敬和设计精良的 C++ 库项目之一。” Herb SutterAndrei Alexandrescu, C++ 编码标准

概述

描述

此库实现了一种类型安全的判别/标记联合类型,variant<T…​>,它与 C++17 标准的 std::variant<T…​> 在 API 上兼容。

一个 variant<T1, T2, …​, Tn> 变量可以保存类型 T1T2、…​、Tn 中的任何值。 例如,variant<int64_t, double, std::string> 可以保存一个 int64_t 值、一个 double 值或一个 string 值。

这种类型有时被称为“标记联合”,因为它大致等同于

struct V
{
    enum tag { tag_int64_t, tag_double, tag_string };

    tag tag_;

    union
    {
        int64_t     i_;
        double      d_;
        std::string s_;
    };
};

使用示例

Variant 可用于表示动态类型的数值。 形式为

server.host=test.example.com
server.port=9174
cache.max_load=0.7

的配置文件可以表示为 std::map<std::string, variant<int64_t, double, std::string>>

Variant 也可以表示多态。 举一个经典的例子,一个多态的形状集合

#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <memory>
#include <cmath>

class Shape
{
public:

    virtual ~Shape() = default;
    virtual double area() const = 0;
};

class Rectangle: public Shape
{
private:

    double width_, height_;

public:

    Rectangle( double width, double height ):
        width_( width ), height_( height ) {}

    virtual double area() const { return width_ * height_; }
};

class Circle: public Shape
{
private:

    double radius_;

public:

    explicit Circle( double radius ): radius_( radius ) {}
    virtual double area() const { return M_PI * radius_ * radius_; }
};

double total_area( std::vector<std::unique_ptr<Shape>> const & v )
{
    double s = 0.0;

    for( auto const& p: v )
    {
        s += p->area();
    }

    return s;
}

int main()
{
    std::vector<std::unique_ptr<Shape>> v;

    v.push_back( std::unique_ptr<Shape>( new Circle( 1.0 ) ) );
    v.push_back( std::unique_ptr<Shape>( new Rectangle( 2.0, 3.0 ) ) );

    std::cout << "Total area: " << total_area( v ) << std::endl;
}

可以替换为 variant<Rectangle, Circle> 值的集合来表示。 这需要提前知道可能的 Shape 类型,通常情况如此。 作为回报,我们不再需要虚函数,也不需要使用 new Rectanglenew Circle 在堆上分配值。

#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <cmath>

#include <boost/variant2/variant.hpp>
using namespace boost::variant2;

struct Rectangle
{
    double width_, height_;
    double area() const { return width_ * height_; }
};

struct Circle
{
    double radius_;
    double area() const { return M_PI * radius_ * radius_; }
};

double total_area( std::vector<variant<Rectangle, Circle>> const & v )
{
    double s = 0.0;

    for( auto const& x: v )
    {
        s += visit( []( auto const& y ){ return y.area(); }, x );
    }

    return s;
}

int main()
{
    std::vector<variant<Rectangle, Circle>> v;

    v.push_back( Circle{ 1.0 } );
    v.push_back( Rectangle{ 2.0, 3.0 } );

    std::cout << "Total area: " << total_area( v ) << std::endl;
}

构造和赋值

如果我们看一下

    v.push_back( Circle{ 1.0 } );

这行代码,我们可以推断出 variant<Rectangle, Circle> 可以从 Circle(和 Rectangle)构造(隐式),并且确实可以。 它也可以被赋值为 CircleRectangle

variant<Rectangle, Circle> v = Circle{ 1.0 }; // v holds Circle
v = Rectangle{ 2.0, 3.0 };                    // v now holds Rectangle

如果我们尝试从既不是 int 也不是 float 的东西(例如 (short)1)构造 variant<int, float>,则行为“如同” variant 声明了两个构造函数,

variant::variant(int x);
variant::variant(float x);

并使用标准重载解析规则来选择将要使用的那个。 因此,variant<int, float>((short)1) 将保存一个 int

检查值

将值放入 variant 很容易,但取出值必然会比较复杂。 variant<int, float> 不可能定义成员函数 get() const,因为此类函数需要在编译时确定其返回类型,而正确的返回类型是 int 还是 float 只能在运行时才知道。

有几种方法可以解决这个问题。 首先,有访问器成员函数

std::size_t variant::index() const noexcept;

它返回当前类型的从零开始的索引。 对于 variant<int, float>,对于 int 它将返回 0,对于 float 它将返回 1

一旦我们有了索引,我们就可以使用自由函数 get<N> 来获取该值。 由于我们将类型索引传递给 get,因此它知道返回什么。 get<0>(v) 将返回 intget<1>(v) 将返回 float

void f( variant<int, float> const& v )
{
    switch( v.index() )
    {
    case 0:

        // use get<0>(v)
        break;

    case 1:

        // use get<1>(v)
        break;

    default:

        assert(false); // never happens
    }
}

如果我们调用 get<0>(v),而 v.index() 当前不是 0,则会抛出异常(类型为 bad_variant_access)。

另一种方法是使用 get<int>(v)get<float>(v)。 这工作方式类似。

另一种避免 bad_variant_access 的可能性方法是使用 get_if。 它不是返回对包含值的引用,而是返回指向它的指针,返回 nullptr 以指示类型不匹配。 get_if 接受指向 variant 的指针,因此在我们的示例中,我们将使用以下内容

void f( variant<int, float> const& v )
{
    if( int const * p = get_if<int>(&v) )
    {
        // use *p
    }
    else if( float const * p = get_if<float>(&v) )
    {
        // use *p
    }
    else
    {
        assert(false); // never happens
    }
}

访问

最后但并非最不重要的是,还有 visitvisit(f, v) 使用 variant v 中包含的值调用函数对象 f 并返回结果。 当 vvariant<int, float> 时,它将使用 intfloat 调用 f。 函数对象必须准备好接受两者。

在实践中,这可以通过让函数采用可以传递 intfloat 的类型(例如 double)来实现。

double f( double x ) { return x; }

double g( variant<int, float> const& v )
{
    return visit( f, v );
}

通过使用具有重载的 operator() 的函数对象

struct F
{
    void operator()(int x) const { /* use x */ }
    void operator()(float x) const { /* use x */ }
};

void g( variant<int, float> const& v )
{
    visit( F(), v );
}

或者通过使用多态 lambda,就像我们在 Circle/Rectangle 示例中所做的那样

void g( variant<int, float> const& v )
{
    visit( [&]( auto const& x ){ std::cout << x << std::endl; }, v );
}

visit 也可以接受多个 variantvisit(f, v1, v2) 调用 f(x1, x2),其中 x1v1 中包含的值,x2v2 中的值。

默认构造

variant 的默认构造函数对列表中的第一个类型进行值初始化。 variant<int, float>{} 保存 0(类型为 int),variant<float, int>{} 保存 0.0f

这通常是期望的行为。 但是,在 variant<std::mutex, std::recursive_mutex> 等情况下,人们可能合理地希望避免默认构造 std::mutex。 提供了一种类型 monostate,可以在这些情况下用作第一种类型。 variant<monostate, std::mutex, std::recursive_mutex> 将默认构造一个 monostate,这基本上是一个空操作,因为 monostate 实际上是一个空的 struct

修订历史

1.88.0 中的变更

  • 对于索引,使用最小的合适无符号类型。

1.83.0 中的变更

  • 添加了 uses_double_storage()

1.81.0 中的变更

  • 添加了对 boost::json::value_fromboost::json::value_to 的支持。

1.79.0 中的变更

  • monostate 添加了 operator<<

1.78.0 中的变更

  • 添加了 <boost/variant2.hpp>

  • 添加了 unsafe_get<I>

  • 添加了 visit_by_index

  • 添加了 operator<<

1.76.0 中的变更

  • 改进了双缓冲情况下的生成代码。

1.74.0 中的变更

  • 添加了对 visit 中派生类型的支持

  • 提高了许多(数百个)替代方案的编译性能。

  • 添加了对 visit<R> 的支持

1.73.0 中的变更

  • 添加了对 std::hashboost::hash 的支持。

  • T…​ 中的所有类型都是 trivial 时,variant<T…​> 现在是 trivial 的。 这通过使其能够传递到函数并从函数返回寄存器来提高性能。

1.71.0 中的变更

在 Boost 正式审查之后,该实现已更改为提供强异常安全保证,而不是基本保证。 expected 已被删除。

设计

特性

variant 实现具有两个显着特征

  • 它永远不会“没有值”,也就是说,variant<T1, T2, …​, Tn> 具有一个不变性,即它始终包含类型 T1T2、…​、Tn 之一的有效值。

  • 它在赋值和 emplace 上提供强异常安全保证。

除非所有包含的类型都具有非抛出的移动构造函数,否则这是通过使用双重存储来实现的。

原理

永不为空值

variant<X, Y, Z> 只能保存 X 类型、Y 类型或 Z 类型的值,而不能保存其他值,这在直觉上是有道理的。

如果我们将 variant 视为 union 的扩展,因为 union 具有一个名为“没有活动成员”的状态,因此可以认为 variant<X, Y, Z> 也应该具有这样的附加状态,而不包含 XYZ 中的任何一个。

然而,这使得 variant 在实践中不太方便,并且作为构建块的用处也较小。 如果我们确实需要一个只保存 XYZ 的变量,则额外的空状态会带来需要解决的复杂性。 并且在我们确实需要这个额外的空状态的情况下,我们可以只使用 variant<empty, X, Y, Z>,并使用合适的 struct empty {};

从纯粹的设计角度来看,没有额外空状态的理由是充分的。 然而,实施方面的考虑则另有说法。

当我们用另一个(类型为 Y)替换 variant 的当前值(例如,类型为 X)时,由于新值需要占用与旧值相同的存储空间,我们需要首先销毁旧的 X,然后在它的位置构造一个新的 Y。 但由于这是 C++,因此构造可能会因异常而失败。 此时,variant 处于我们已同意它不能处于的“没有活动成员”状态。

这是一个合理的问题,正是这个问题使得拥有一个空的/无值的状态如此吸引人。 我们只需在异常时将 variant 保持为空即可。

不过,如上所述,从设计的角度来看,这是不可取的,因为它使组件的用处减少并且不够优雅。

有几种方法可以解决这个问题。 最直接的方法是禁止构造可能抛出的类型。 由于我们总是可以先创建一个临时值,然后使用移动构造函数来初始化 variant 中的值,因此只需要非抛出的移动构造函数,而不是所有构造函数都必须是非抛出的。

不幸的是,在至少一种流行的标准库实现中,基于节点的容器(如 std::liststd::map)可能具有抛出移动构造函数。 禁止 variant<X, std::map<Y, Z>> 几乎是不切实际的,因此无法避免异常情况。

在发生异常时,我们还可以构造一些其他值,使 variant 有效;但在一般情况下,该构造也可能抛出。 如果其中一种类型具有非抛出的默认构造函数,我们可以使用它;但如果没有,我们就不能。

Boost.Variant 在此采取的方法是在堆上分配该值的临时副本。 在发生异常时,可以将指向该临时副本的指针存储到 variant 中。 指针操作不会抛出异常。

另一种选择是使用双缓冲。 如果我们的 variant 占用两倍的存储空间,我们可以在未使用的半部分中构造新值,然后在构造成功后,销毁另一半中的旧值。

std::variant 被标准化时,这些方法都被认为是不受欢迎的,因为它们要么引入开销,要么对 variant 可以包含的类型过于严格。 因此,作为一种折衷方案,std::variant 采取了一种可以(不客气地)描述为“既要又要”的方式。

由于所描述的异常情况相对罕见,std::variant 有一个特殊情况,称为“无值(valueless)”,当发生异常时会进入这种状态,但接口尽可能少地承认它的存在,允许用户假装它不存在。

从实际的角度来看,这可能不算太糟糕,但这让许多人感到不满意。 "从不"发生的罕见状态往往未经充分测试,而当 "从不" 真的发生时,通常是在最不方便的时候。

此实现不遵循 std::variant;它静态地保证 variant 永远不会处于无值状态。 提供函数 valueless_by_exception 是为了兼容性,但它始终返回 false

相反,如果包含的类型使得在更改包含的值时无法避免异常情况,则使用双重存储。

强异常安全性

最初的提交仅提供了基本的异常安全保证。 如果尝试通过赋值或 emplace 更改包含的值失败并抛出异常,并且替代方案中存在具有非抛出默认构造函数的类型,则会在 variant 中创建一个该类型的值。 这种决定的好处是,对双重存储的需求减少了。

审查员们几乎一致地讨厌它。 构造一个随机类型被认为太不可预测,并且不符合基本保证的精神。 即使非抛出,所选类型的默认构造函数可能仍然具有不良的副作用。 或者,如果没有这种情况,该类型的值可能对周围的代码具有特殊的意义。 因此,有人认为,variant 应该保持其旧值,或过渡到新值,而不合成其他状态。

另一方面,有些人认为双重存储是不可接受的。 但他们认为原则上不可接受,而不管其使用频率如何。

因此,在赋值和 emplace 上提供强异常安全保证被声明为接受条件。

回顾起来,这是一个正确的决定。 通常不提供强保证的原因是它不具有组合性。 当 XY 在赋值时提供基本保证时,struct { X x; Y y; }; 也是如此。 类似地,当 XY 具有非抛出赋值时,struct 也是如此。 但这对强保证不成立。

通常的做法是在赋值时提供基本保证,并让用户通过非抛出的 swap 或非抛出的移动赋值来合成一个“强”赋值。 也就是说,给定类型为 Xx1x2,而不是“基本”的 x1 = x2;,可以使用 X(x2).swap(x1);x1 = X(x2);

几乎所有类型都提供非抛出的 swap 或非抛出的移动赋值,因此这效果很好。 几乎所有,除了 variant,它在一般情况下既没有非抛出的 swap 也没有非抛出的移动赋值。 如果 variant 本身不提供强保证,用户就不可能合成它。

所以它应该提供,而且它也确实提供了。

与 std::variant 的区别

此实现与 std::variant 之间的主要区别是

  • 没有 valueless-by-exception 状态:valueless_by_exception() 始终返回 false

  • 赋值和 emplace 上的强异常安全保证。

  • emplace 首先构造新值,然后销毁旧值; 在单存储的情况下,这转化为构造一个临时对象,然后将其移动到位。

  • 提供了一个转换构造函数,例如从 variant<int, float>variant<float, double, int>,作为一个扩展。

  • 相反的操作,从 variant<float, double, int>variant<int, float>,作为成员函数 subset<U…​> 提供。(如果变体的当前状态无法表示,此操作可能会抛出异常。)

  • unsafe_get,一种未经检查的 getget_if 的替代方案,作为扩展提供。

  • visit_by_index,一个访问函数,它接受一个变体和多个函数对象,每个替代方案一个,作为扩展提供。

  • C++20 对 std::variant 的添加和更改尚未实现。

与 Boost.Variant 的区别

此库与 std::variant API 兼容。 因此,它的接口与 Boost.Variant 的不同。 例如,访问是通过 visit 而不是 apply_visitor 执行的。

不支持递归变体。

使用双重存储而不是临时堆备份。 此 variant 始终是“基于堆栈的”,它从不分配,并且从不自行抛出 bad_alloc

实现

依赖

此实现仅依赖于 Boost.Config、Boost.Assert 和 Boost.Mp11。

支持的编译器

  • 带有 -std=c++11 或更高版本的 GCC 4.8 或更高版本

  • 带有 -std=c++11 或更高版本的 Clang 3.9 或更高版本

  • Visual Studio 2015 或更高版本

Github ActionsAppveyor 上测试。

参考

<boost/variant2/variant.hpp>

概要

namespace boost {
namespace variant2 {

// in_place_type

template<class T> struct in_place_type_t {};
template<class T> constexpr in_place_type_t<T> in_place_type{};

// in_place_index

template<std::size_t I> struct in_place_index_t {};
template<std::size_t I> constexpr in_place_index_t<I> in_place_index{};

// variant

template<class... T> class variant;

// variant_size

template<class T> struct variant_size {};

template<class T> struct variant_size<T const>: variant_size<T> {};
template<class T> struct variant_size<T volatile>: variant_size<T> {};
template<class T> struct variant_size<T const volatile>: variant_size<T> {};

template<class T> struct variant_size<T&>: variant_size<T> {}; // extension
template<class T> struct variant_size<T&&>: variant_size<T> {}; // extension

template<class T>
  inline constexpr size_t variant_size_v = variant_size<T>::value;

template<class... T>
  struct variant_size<variant<T...>>:
    std::integral_constant<std::size_t, sizeof...(T)> {};

// variant_alternative

template<size_t I, class T> struct variant_alternative {};

template<size_t I, class T> struct variant_alternative<I, T const>;
template<size_t I, class T> struct variant_alternative<I, T volatile>;
template<size_t I, class T> struct variant_alternative<I, T const volatile>;

template<size_t I, class T> struct variant_alternative<I, T&>; // extension
template<size_t I, class T> struct variant_alternative<I, T&&>; // extension

template<size_t I, class T>
  using variant_alternative_t = typename variant_alternative<I, T>::type;

template<size_t I, class... T>
  struct variant_alternative<I, variant<T...>>;

// variant_npos

constexpr std::size_t variant_npos = -1;

// holds_alternative

template<class U, class... T>
  constexpr bool holds_alternative(const variant<T...>& v) noexcept;

// get

template<size_t I, class... T>
  constexpr variant_alternative_t<I, variant<T...>>&
    get(variant<T...>& v);
template<size_t I, class... T>
  constexpr variant_alternative_t<I, variant<T...>>&&
    get(variant<T...>&& v);
template<size_t I, class... T>
  constexpr const variant_alternative_t<I, variant<T...>>&
    get(const variant<T...>& v);
template<size_t I, class... T>
  constexpr const variant_alternative_t<I, variant<T...>>&&
    get(const variant<T...>&& v);

template<class U, class... T>
  constexpr U& get(variant<T...>& v);
template<class U, class... T>
  constexpr U&& get(variant<T...>&& v);
template<class U, class... T>
  constexpr const U& get(const variant<T...>& v);
template<class U, class... T>
  constexpr const U&& get(const variant<T...>&& v);

// get_if

template<size_t I, class... T>
  constexpr add_pointer_t<variant_alternative_t<I, variant<T...>>>
    get_if(variant<T...>* v) noexcept;
template<size_t I, class... T>
  constexpr add_pointer_t<const variant_alternative_t<I, variant<T...>>>
    get_if(const variant<T...>* v) noexcept;

template<class U, class... T>
  constexpr add_pointer_t<U>
    get_if(variant<T...>* v) noexcept;
template<class U, class... T>
  constexpr add_pointer_t<const U>
    get_if(const variant<T...>* v) noexcept;

// unsafe_get (extension)

template<size_t I, class... T>
  constexpr variant_alternative_t<I, variant<T...>>&
    unsafe_get(variant<T...>& v);
template<size_t I, class... T>
  constexpr variant_alternative_t<I, variant<T...>>&&
    unsafe_get(variant<T...>&& v);
template<size_t I, class... T>
  constexpr const variant_alternative_t<I, variant<T...>>&
    unsafe_get(const variant<T...>& v);
template<size_t I, class... T>
  constexpr const variant_alternative_t<I, variant<T...>>&&
    unsafe_get(const variant<T...>&& v);

// relational operators

template<class... T>
  constexpr bool operator==(const variant<T...>& v, const variant<T...>& w);
template<class... T>
  constexpr bool operator!=(const variant<T...>& v, const variant<T...>& w);
template<class... T>
  constexpr bool operator<(const variant<T...>& v, const variant<T...>& w);
template<class... T>
  constexpr bool operator>(const variant<T...>& v, const variant<T...>& w);
template<class... T>
  constexpr bool operator<=(const variant<T...>& v, const variant<T...>& w);
template<class... T>
  constexpr bool operator>=(const variant<T...>& v, const variant<T...>& w);

// swap

template<class... T>
  void swap(variant<T...>& v, variant<T...>& w) noexcept( /*see below*/ );

// visit

template<class R = /*unspecified*/, class F, class... V>
  constexpr /*see below*/ visit(F&& f, V&&... v);

// visit_by_index (extension)

template<class R = /*unspecified*/, class V, class... F>
  constexpr /*see below*/ visit_by_index(V&& v, F&&... f);

// monostate

struct monostate {};

constexpr bool operator==(monostate, monostate) noexcept { return true; }
constexpr bool operator!=(monostate, monostate) noexcept { return false; }
constexpr bool operator<(monostate, monostate) noexcept { return false; }
constexpr bool operator>(monostate, monostate) noexcept { return false; }
constexpr bool operator<=(monostate, monostate) noexcept { return true; }
constexpr bool operator>=(monostate, monostate) noexcept { return true; }

// stream insertion (extension)

template<class Ch, class Tr, class... T>
  std::basic_ostream<Ch, Tr>&
    operator<<( std::basic_ostream<Ch, Tr>& os, variant<T...> const& v );

template<class Ch, class Tr>
  std::basic_ostream<Ch, Tr>&
    operator<<( std::basic_ostream<Ch, Tr>& os, monostate const& v );

// bad_variant_access

class bad_variant_access;

} // namespace variant2
} // namespace boost

variant

namespace boost {
namespace variant2 {

template<class... T> class variant
{
public:

  // constructors

  constexpr variant() noexcept( /*see below*/ );

  constexpr variant( variant const & r ) noexcept( /*see below*/ );
  constexpr variant( variant&& r ) noexcept( /*see below*/ );

  template<class U>
    constexpr variant( U&& u ) noexcept( /*see below*/ );

  template<class U, class... A>
    constexpr explicit variant( in_place_type_t<U>, A&&... a );
  template<class U, class V, class... A>
    constexpr explicit variant( in_place_type_t<U>,
      std::initializer_list<V> il, A&&... a );

  template<size_t I, class... A>
    constexpr explicit variant( in_place_index_t<I>, A&&... a );
  template<size_t I, class V, class... A>
    constexpr explicit variant( in_place_index_t<I>,
      std::initializer_list<V> il, A&&... a );

  // destructor

  ~variant();

  // assignment

  constexpr variant& operator=( variant const & r ) noexcept( /*see below*/ );
  constexpr variant& operator=( variant&& r ) noexcept( /*see below*/ );

  template<class U> constexpr variant& operator=( U&& u ) noexcept( /*see below*/ );

  // modifiers

  template<class U, class... A>
    constexpr U& emplace( A&&... a );
  template<class U, class V, class... A>
    constexpr U& emplace( std::initializer_list<V> il, A&&... a );

  template<size_t I, class... A>
    constexpr variant_alternative_t<I, variant<T...>>&
      emplace( A&&... a );
  template<size_t I, class V, class... A>
    constexpr variant_alternative_t<I, variant<T...>>&
      emplace( std::initializer_list<V> il, A&&... a );

  // value status

  constexpr bool valueless_by_exception() const noexcept;
  constexpr size_t index() const noexcept;

  static constexpr bool uses_double_storage() noexcept;

  // swap

  void swap( variant& r ) noexcept( /*see below*/ );

  // converting constructors (extension)

  template<class... U> variant( variant<U...> const& r )
    noexcept( /*see below*/ );

  template<class... U> variant( variant<U...>&& r )
    noexcept( /*see below*/ );

  // subset (extension)

  template<class... U> constexpr variant<U...> subset() & ;
  template<class... U> constexpr variant<U...> subset() && ;
  template<class... U> constexpr variant<U...> subset() const& ;
  template<class... U> constexpr variant<U...> subset() const&& ;
};

} // namespace variant2
} // namespace boost

在以下描述中,设 i[0, sizeof…​(T)) 范围内,并且 TiT…​ 中的第 i 个类型。

构造函数
constexpr variant() noexcept( std::is_nothrow_default_constructible_v<T0> );
  • 影响

    构造一个 variant,其中包含类型为 T0 的值初始化值。

    确保

    index() == 0.

    抛出

    T0 的值初始化抛出的任何异常。

    备注

    除非 std::is_default_constructible_v<T0>true,否则此函数不参与重载解析。

constexpr variant( variant const & w )
  noexcept( mp_all<std::is_nothrow_copy_constructible<T>...>::value );
  • 影响

    初始化变体以保存与 w 相同的备选项和值。

    抛出

    包含的值的初始化抛出的任何异常。

    备注

    除非所有 istd::is_copy_constructible_v<Ti>true,否则此函数不参与重载解析。

constexpr variant( variant&& w )
  noexcept( mp_all<std::is_nothrow_move_constructible<T>...>::value );
  • 影响

    初始化变体以保存与 w 相同的备选项和值。

    抛出

    包含的值的移动初始化抛出的任何异常。

    备注

    除非所有 istd::is_move_constructible_v<Ti>true,否则此函数不参与重载解析。

template<class U> constexpr variant( U&& u ) noexcept(/*see below*/);
  • Tj 是如下确定的类型:为每个备选类型 Ti 构建一个虚构函数 FUN(Ti)。 通过重载解析为表达式 FUN(std::forward<U>(u)) 选择的重载 FUN(Tj) 定义了备选项 Tj,它是构造后包含的值的类型。

    影响

    初始化 *this 以保存备选类型 Tj,并从 std::forward<U>(u) 初始化包含的值。

    确保

    holds_alternative<Tj>(*this).

    抛出

    包含的值的初始化抛出的任何异常。

    备注

    noexcept 内部的表达式等效于 std::is_nothrow_constructible_v<Tj, U>。 除非以下情况,否则此函数不参与重载解析

    • sizeof…​(T) 为非零,

    • std::is_same_v<std::remove_cvref_t<U>, variant>false

    • std::remove_cvref_t<U> 既不是 in_place_type_t 的特化,也不是 in_place_index_t 的特化,

    • std::is_constructible_v<Tj, U>true,并且

    • 表达式 FUN(std::forward<U>(u)) 格式良好。

template<class U, class... A>
  constexpr explicit variant( in_place_type_t<U>, A&&... a );
  • 影响

    使用参数 std::forward<A>(a)…​ 初始化类型为 U 的包含的值。

    确保

    holds_alternative<U>(*this).

    抛出

    包含的值的初始化抛出的任何异常。

    备注

    除非 T…​ 中恰好出现一次 Ustd::is_constructible_v<U, A…​> 为 true,否则此函数不参与重载解析。

template<class U, class V, class... A>
  constexpr explicit variant( in_place_type_t<U>, std::initializer_list<V> il,
    A&&... a );
  • 影响

    使用参数 ilstd::forward<A>(a)…​ 初始化类型为 U 的包含的值。

    确保

    holds_alternative<U>(*this).

    抛出

    包含的值的初始化抛出的任何异常。

    备注

    除非 T…​ 中恰好出现一次 Ustd::is_constructible_v<U, initializer_list<V>&, A…​>true,否则此函数不参与重载解析。

template<size_t I, class... A>
  constexpr explicit variant( in_place_index_t<I>, A&&... a );
  • 影响

    使用参数 std::forward<A>(a)…​ 初始化类型为 TI 的包含的值。

    确保

    index() == I.

    抛出

    包含的值的初始化抛出的任何异常。

    备注

    除非 I < sizeof…​(T)std::is_constructible_v<TI, A…​>true,否则此函数不参与重载解析。

template<size_t I, class V, class... A>
  constexpr explicit variant( in_place_index_t<I>, std::initializer_list<V> il,
    A&&... a );
  • 影响

    使用参数 ilstd::forward<A>(a)…​ 初始化类型为 TI 的包含的值。

    确保

    index() == I.

    抛出

    包含的值的初始化抛出的任何异常。

    备注

    除非 I < sizeof…​(T)std::is_constructible_v<TI, initializer_list<V>&, A…​>true,否则此函数不参与重载解析。

析构函数
~variant();
  • 影响

    销毁当前包含的值。

赋值
constexpr variant& operator=( const variant& r )
  noexcept( mp_all<std::is_nothrow_copy_constructible<T>...>::value );
  • jr.index()

    影响

    emplace<j>(get<j>(r)).

    返回

    *this.

    确保

    index() == r.index().

    备注

    除非所有 istd::is_copy_constructible_v<Ti> && std::is_copy_assignable_v<Ti>true,否则此运算符不参与重载解析。

constexpr variant& operator=( variant&& r )
  noexcept( mp_all<std::is_nothrow_move_constructible<T>...>::value );
  • jr.index()

    影响

    emplace<j>(get<j>(std::move(r))).

    返回

    *this.

    确保

    index() == r.index().

    备注

    除非所有 istd::is_move_constructible_v<Ti> && std::is_move_assignable_v<Ti>true,否则此运算符不参与重载解析。

template<class U> constexpr variant& operator=( U&& u )
  noexcept( /*see below*/ );
  • Tj 是如下确定的类型:为每个备选类型 Ti 构建一个虚构函数 FUN(Ti)。 通过重载解析为表达式 FUN(std::forward<U>(u)) 选择的重载 FUN(Tj) 定义了备选项 Tj,它是构造后包含的值的类型。

    影响

    emplace<j>(std::forward<U>(u)).

    返回

    *this.

    确保

    index() == j.

    备注

    noexcept 内部的表达式是 std::is_nothrow_constructible_v<Tj, U&&>。 除非以下情况,否则此运算符不参与重载解析

    • std::is_same_v<std::remove_cvref_t<T>, variant>false

    • std::is_constructible_v<Tj, U&&> && std::is_assignable_v<Tj&, U&&>true,并且

    • 表达式 FUN(std::forward<U>(u))(其中 FUN 是上述虚构函数集)格式良好。

修饰符
template<class U, class... A>
  constexpr U& emplace( A&&... a );
  • IUT…​ 中的从零开始的索引。

    影响

    等效于:return emplace<I>(std::forward<A>(a)…​);

    备注

    除非 std::is_constructible_v<U, A&&…​>trueUT…​ 中恰好出现一次,否则此函数不应参与重载解析。

template<class U, class V, class... A>
  constexpr U& emplace( std::initializer_list<V> il, A&&... a );
  • IUT…​ 中的从零开始的索引。

    影响

    等效于:return emplace<I>(il, std::forward<A>(a)…​);

    备注

    除非 std::is_constructible_v<U, std::initializer_list<V>&, A&&…​>trueUT…​ 中恰好出现一次,否则此函数不应参与重载解析。

template<size_t I, class... A>
  constexpr variant_alternative_t<I, variant<T...>>&
    emplace( A&&... a );
  • 要求

    I < sizeof…​(T).

    影响

    初始化一个新的包含的值,就像使用表达式 Ti(std::forward<A>(a)…​) 一样,然后销毁当前包含的值。

    确保

    index() == I.

    返回

    对新的包含的值的引用。

    抛出

    除非新包含的值的初始化抛出异常,否则什么也不做。

    异常安全

    强。 发生异常时,包含的值不会更改。

    备注

    除非 std::is_constructible_v<Ti, A&&…​>true,否则此函数不应参与重载解析。

template<size_t I, class V, class... A>
  constexpr variant_alternative_t<I, variant<T...>>&
    emplace( std::initializer_list<V> il, A&&... a );
  • 要求

    I < sizeof…​(T).

    影响

    初始化一个新的包含的值,就像使用表达式 Ti(il, std::forward<A>(a)…​) 一样,然后销毁当前包含的值。

    确保

    index() == I.

    返回

    对新的包含的值的引用。

    抛出

    除非新包含的值的初始化抛出异常,否则什么也不做。

    异常安全

    强。 发生异常时,包含的值不会更改。

    备注

    除非 std::is_constructible_v<Ti, std::initializer_list<V>&, A&&…​>true,否则此函数不应参与重载解析。

值状态
constexpr bool valueless_by_exception() const noexcept;
  • 返回

    false.

注意
提供此函数纯粹是为了与 std::variant 兼容。
constexpr size_t index() const noexcept;
  • 返回

    活动备选项的从零开始的索引。

static constexpr bool uses_double_storage() noexcept;
  • 返回

    如果变体使用双重存储来满足永不无值的保证,因为其中一个备选项不是非抛出移动构造的,则为 true,否则为 false

交换
void swap( variant& r ) noexcept( mp_all<std::is_nothrow_move_constructible<T>...,
  is_nothrow_swappable<T>...>::value );
  • 影响
    • 如果 index() == r.index(),则调用 swap(get<I>(*this), get<I>(r)),其中 Iindex()

    • 否则,就像 variant tmp(std::move(*this)); *this = std::move(r); r = std::move(tmp); 一样

转换构造函数(扩展)
template<class... U> variant( variant<U...> const& r )
  noexcept( mp_all<std::is_nothrow_copy_constructible<U>...>::value );
  • 影响

    r 的包含的值初始化包含的值。

    抛出

    包含的值的初始化抛出的任何异常。

    备注

    除非 U…​ 中的所有类型都在 T…​ 中,并且所有 Uistd::is_copy_constructible_v<Ui>::valuetrue,否则此函数不参与重载解析。

template<class... U> variant( variant<U...>&& r )
  noexcept( mp_all<std::is_nothrow_move_constructible<U>...>::value );
  • 影响

    std::move(r) 的包含的值初始化包含的值。

    抛出

    包含的值的初始化抛出的任何异常。

    备注

    除非 U…​ 中的所有类型都在 T…​ 中,并且所有 Uistd::is_move_constructible_v<Ui>::valuetrue,否则此函数不参与重载解析。

子集(扩展)
template<class... U> constexpr variant<U...> subset() & ;
template<class... U> constexpr variant<U...> subset() const& ;
  • 返回

    一个 variant<U…​>,其包含的值从 *this 的包含的值进行复制初始化,并且具有相同的类型。

    抛出
    • 如果 *this 的活动备选项不在 U…​ 中的类型中,则抛出 bad_variant_access

    • 否则,包含的值的初始化抛出的任何异常。

    备注

    除非 U…​ 中的所有类型都在 T…​ 中,并且所有 Uistd::is_copy_constructible_v<Ui>::valuetrue,否则此函数不参与重载解析。

template<class... U> constexpr variant<U...> subset() && ;
template<class... U> constexpr variant<U...> subset() const&& ;
  • 返回

    一个 variant<U…​>,其包含的值从 *this 的包含的值进行移动初始化,并且具有相同的类型。

    抛出
    • 如果 *this 的活动备选项不在 U…​ 中的类型中,则抛出 bad_variant_access

    • 否则,包含的值的初始化抛出的任何异常。

    备注

    除非 U…​ 中的所有类型都在 T…​ 中,并且所有 Uistd::is_move_constructible_v<Ui>::valuetrue,否则此函数不参与重载解析。

variant_alternative

template<size_t I, class T> struct variant_alternative<I, T const>;
template<size_t I, class T> struct variant_alternative<I, T volatile>;
template<size_t I, class T> struct variant_alternative<I, T const volatile>;
template<size_t I, class T> struct variant_alternative<I, T&>; // extension
template<size_t I, class T> struct variant_alternative<I, T&&>; // extension
  • 如果 typename variant_alternative<I, T>::type 存在且为 U

    • variant_alternative<I, T const>::typeU const

    • variant_alternative<I, T volatile>::typeU volatile

    • variant_alternative<I, T const volatile>::typeU const volatile

    • variant_alternative<I, T&>::typeU&

    • variant_alternative<I, T&&>::typeU&&

    否则,这些结构体没有成员 type

template<size_t I, class... T>
  struct variant_alternative<I, variant<T...>>;
  • I < sizeof…​(T) 时,嵌套类型 typeT…​ 中第 I 个(从零开始)类型的别名。否则,没有成员 type

holds_alternative

template<class U, class... T>
  constexpr bool holds_alternative(const variant<T...>& v) noexcept;
  • 要求

    类型 UT…​ 中恰好出现一次。否则,程序格式错误。

    返回

    如果 index() 等于 T…​U 的从零开始的索引,则为 true

get

template<size_t I, class... T>
  constexpr variant_alternative_t<I, variant<T...>>&
    get(variant<T...>& v);
template<size_t I, class... T>
  constexpr variant_alternative_t<I, variant<T...>>&&
    get(variant<T...>&& v);
template<size_t I, class... T>
  constexpr const variant_alternative_t<I, variant<T...>>&
    get(const variant<T...>& v);
template<size_t I, class... T>
  constexpr const variant_alternative_t<I, variant<T...>>&&
    get(const variant<T...>&& v);
  • 影响

    如果 v.index()I,则返回对变体中存储的对象的引用。否则,抛出 bad_variant_access 异常。

    备注

    除非 I < sizeof…​(T),否则这些函数不参与重载解析。

template<class U, class... T>
  constexpr U& get(variant<T...>& v);
template<class U, class... T>
  constexpr U&& get(variant<T...>&& v);
template<class U, class... T>
  constexpr const U& get(const variant<T...>& v);
template<class U, class... T>
  constexpr const U&& get(const variant<T...>&& v);
  • 要求

    类型 UT…​ 中恰好出现一次。否则,程序格式错误。

    影响

    如果 v 保存了类型为 U 的值,则返回对该值的引用。否则,抛出 bad_variant_access 异常。

get_if

template<size_t I, class... T>
  constexpr add_pointer_t<variant_alternative_t<I, variant<T...>>>
    get_if(variant<T...>* v) noexcept;
template<size_t I, class... T>
  constexpr add_pointer_t<const variant_alternative_t<I, variant<T...>>>
    get_if(const variant<T...>* v) noexcept;
  • 影响

    指向变体中存储的值的指针,如果 v != nullptr && v->index() == I。否则,为 nullptr

    备注

    除非 I < sizeof…​(T),否则这些函数不参与重载解析。

template<class U, class... T>
  constexpr add_pointer_t<U>
    get_if(variant<T...>* v) noexcept;
template<class U, class... T>
  constexpr add_pointer_t<const U>
    get_if(const variant<T...>* v) noexcept;
  • 要求

    类型 UT…​ 中恰好出现一次。否则,程序格式错误。

    影响

    等效于:return get_if<I>(v);,其中 IT…​U 的从零开始的索引。

unsafe_get(扩展)

template<size_t I, class... T>
  constexpr variant_alternative_t<I, variant<T...>>&
    unsafe_get(variant<T...>& v);
template<size_t I, class... T>
  constexpr variant_alternative_t<I, variant<T...>>&&
    unsafe_get(variant<T...>&& v);
template<size_t I, class... T>
  constexpr const variant_alternative_t<I, variant<T...>>&
    unsafe_get(const variant<T...>& v);
template<size_t I, class... T>
  constexpr const variant_alternative_t<I, variant<T...>>&&
    unsafe_get(const variant<T...>&& v);
  • 要求

    v.index() == I.

    返回

    对变体中存储的对象的引用。

关系运算符

template<class... T>
  constexpr bool operator==(const variant<T...>& v, const variant<T...>& w);
  • 返回

    v.index() == w.index() && get<I>(v) == get<I>(w),其中 Iv.index()

template<class... T>
  constexpr bool operator!=(const variant<T...>& v, const variant<T...>& w);
  • 返回

    !(v == w).

template<class... T>
  constexpr bool operator<(const variant<T...>& v, const variant<T...>& w);
  • 返回

    v.index() < w.index() || (v.index() == w.index() && get<I>(v) < get<I>(w)),其中 Iv.index()

template<class... T>
  constexpr bool operator>(const variant<T...>& v, const variant<T...>& w);
  • 返回

    w < v.

template<class... T>
  constexpr bool operator<=(const variant<T...>& v, const variant<T...>& w);
  • 返回

    v.index() < w.index() || (v.index() == w.index() && get<I>(v) <= get<I>(w)),其中 Iv.index()

template<class... T>
  constexpr bool operator>=(const variant<T...>& v, const variant<T...>& w);
  • 返回

    w <= v.

swap

template<class... T>
  void swap(variant<T...>& v, variant<T...>& w) noexcept( /*see below*/ );
  • 影响

    等效于 v.swap(w)

visit

template<class R = /*unspecified*/, class F, class... V>
  constexpr /*see below*/ visit(F&& f, V&&... v);
  • 返回

    std::forward<F>(f)(get<I>(std::forward<V>(v))…​),其中 I…​v.index()…​

    备注

    如果显式给出了 R,例如 visit<int>,则返回类型为 R。 否则,它从 F 推导。 要使此推导成功,将 F 应用于变体替代项的所有可能方式都必须具有相同的返回类型。

visit_by_index(扩展)

template<class R = /*unspecified*/, class V, class... F>
  constexpr /*see below*/ visit_by_index(V&& v, F&&... f);
  • 要求

    variant_size<V>::value == sizeof…​(F),否则程序格式错误。

    返回

    std::forward<Fi>(fi)(get<i>(std::forward<V>(v))),其中 iv.index() 并且 Fifi 分别是 F…​f…​ 的第 i 个元素。

    备注

    如果显式给出了 R,例如 visit_by_index<int>,则返回类型为 R。否则,它从 F…​V 推导。 将 Fi 应用于相应变体替代项的所有方式都必须具有相同的返回类型,才能使此推导成功。

流插入(扩展)

template<class Ch, class Tr, class... T>
  std::basic_ostream<Ch, Tr>&
    operator<<( std::basic_ostream<Ch, Tr>& os, variant<T...> const& v );
  • 要求

    sizeof…​(T) != 0.

    返回

    os << get<I>(v),其中 Iv.index()

template<class Ch, class Tr>
  std::basic_ostream<Ch, Tr>&
    operator<<( std::basic_ostream<Ch, Tr>& os, monostate const& v );
  • 影响

    os << "monostate".

    返回

    os.

bad_variant_access

class bad_variant_access: public std::exception
{
public:

    bad_variant_access() noexcept = default;

    char const * what() const noexcept
    {
        return "bad_variant_access";
    }
};

<boost/variant2.hpp>

这个便利的头文件包含了 <boost/variant2/variant.hpp>

本文档版权归 Peter Dimov 所有,版权所有 2018, 2019,并根据 Boost Software License, Version 1.0 分发。