////
Copyright 2018, 2019 Peter Dimov

Distributed under the Boost Software License, Version 1.0.

See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt
////

[#overview]
# Overview
:idprefix: overview_

## Description

This library implements a type-safe discriminated/tagged union type,
`variant<T...>`, that is API-compatible with the {cpp}17 Standard's
http://en.cppreference.com/w/cpp/utility/variant[`std::variant<T...>`].

A `variant<T1, T2, ..., Tn>` variable can hold a value of any of the
types `T1`, `T2`, ..., `Tn`. For example,
`variant<int64_t, double, std::string>` can hold an `int64_t` value, a
`double` value, or a `string` value.

Such a type is sometimes called a "tagged union", because it's roughly
equivalent to

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

    tag tag_;

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

## Usage Examples

Variants can be used to represent dynamically-typed values. A configuration
file of the form

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

can be represented as `std::map<std::string, variant<int64_t, double,
std::string>>`.

Variants can also represent polymorphism. To take a classic example, a
polymorphic collection of shapes:

```
#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;
}
```

can instead be represented as a collection of `variant<Rectangle, Circle>`
values. This requires the possible `Shape` types be known in advance, as is
often the case. In return, we no longer need virtual functions, or to allocate
the values on the heap with `new Rectangle` and `new 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;
}
```

## Construction and Assignment

If we look at the

```
    v.push_back( Circle{ 1.0 } );
```

line, we can deduce that `variant<Rectangle, Circle>` can be (implicitly)
constructed from `Circle` (and `Rectangle`), and indeed it can. It can also
be assigned a `Circle` or a `Rectangle`:

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

If we try to construct `variant<int, float>` from something that is neither
`int` nor `float`, say, `(short)1`, the behavior is "as if" the `variant` has
declared two constructors,

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

and the standard overload resolution rules are used to pick the one that will
be used. So `variant<int, float>((short)1)` will hold an `int`.

## Inspecting the Value

Putting values into a `variant` is easy, but taking them out is necessarily a
bit more convoluted. It's not possible for `variant<int, float>` to define a
member function `get() const`, because such a function will need its return
type fixed at compile time, and whether the correct return type is `int` or
`float` will only become known at run time.

There are a few ways around that. First, there is the accessor member function

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

that returns the zero-based index of the current type. For `variant<int,
float>`, it will return `0` for `int` and `1` for `float`.

Once we have the index, we can use the free function `get<N>` to obtain the
value. Since we're passing the type index to `get`, it knows what to return.
`get<0>(v)` will return `int`, and `get<1>(v)` will return `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
    }
}
```

If we call `get<0>(v)`, and `v.index()` is not currently `0`, an exception
(of type `bad_variant_access`) will be thrown.

An alternative approach is to use `get<int>(v)` or `get<float>(v)`. This
works similarly.

Another alternative that avoids the possibility of `bad_variant_access` is
to use `get_if`. Instead of a reference to the contained value, it returns
a pointer to it, returning `nullptr` to indicate type mismatch. `get_if`
takes a pointer to the `variant`, so in our example we'll use something along
the following lines:

```
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
    }
}
```

## Visitation

Last but not least, there's `visit`. `visit(f, v)` calls the a function object
`f` with the value contained in the `variant` `v` and returns the result. When
`v` is `variant<int, float>`, it will call `f` with either an `int` or a
`float`. The function object must be prepared to accept both.

In practice, this can be achieved by having the function take a type that can
be passed either `int` or `float`, such as `double`:

```
double f( double x ) { return x; }

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

By using a function object with an overloaded `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 );
}
```

Or by using a polymorphic lambda, as we did in our `Circle`/`Rectangle`
example:

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

`visit` can also take more than one `variant`. `visit(f, v1, v2)` calls
`f(x1, x2)`, where `x1` is the value contained in `v1` and `x2` is the value
in `v2`.

## Default Construction

The default constructor of `variant` value-initializes the first type in
the list. `variant<int, float>{}` holds `0` (of type `int`), and
`variant<float, int>{}` holds `0.0f`.

This is usually the desired behavior. However, in cases such as
`variant<std::mutex, std::recursive_mutex>`, one might legitimately wish to
avoid constructing a `std::mutex` by default. A provided type, `monostate`,
can be used as the first type in those scenarios. `variant<monostate,
std::mutex, std::recursive_mutex>` will default-construct a `monostate`,
which is basically a no-op, as `monostate` is effectively an empty `struct`.
