Overview

This library implements a type-safe discriminated/tagged union type, variant<T…​>, that is API-compatible with the C++17 Standard’s 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_;
    };
};

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

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.

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

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.

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.

Reference

<boost/variant2/variant.hpp>

Synopsis

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;

// 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);

// visit

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

// 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; }

// swap

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

// 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;

  // 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
Constructors
constexpr variant() noexcept( std::is_nothrow_default_constructible_v<T0> );
  • Effects:

    Constructs a variant holding a value-initialized value of type T0.

    Ensures:

    index() == 0.

    Throws:

    Any exception thrown by the value-initialization of T0.

    Remarks:

    This function does not participate in overload resolution unless std::is_default_constructible_v<T0> is true.

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

    Initializes the variant to hold the same alternative and value as w.

    Throws:

    Any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless std::is_copy_constructible_v<Ti> is true for all i.

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

    Initializes the variant to hold the same alternative and value as w.

    Throws:

    Any exception thrown by the move-initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless std::is_move_constructible_v<Ti> is true for all i.

template<class U> constexpr variant( U&& u ) noexcept(/*see below*/);
  • Let Tj be a type that is determined as follows: build an imaginary function FUN(Ti) for each alternative type Ti. The overload FUN(Tj) selected by overload resolution for the expression FUN(std::forward<U>(u)) defines the alternative Tj which is the type of the contained value after construction.

    Effects:

    Initializes *this to hold the alternative type Tj and initializes the contained value from std::forward<U>(u).

    Ensures:

    holds_alternative<Tj>(*this).

    Throws:

    Any exception thrown by the initialization of the contained value.

    Remarks:

    The expression inside noexcept is equivalent to std::is_nothrow_constructible_v<Tj, U>. This function does not participate in overload resolution unless

    • sizeof…​(T) is nonzero,

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

    • std::remove_cvref_t<U> is neither a specialization of in_place_type_t nor a specialization of in_place_index_t,

    • std::is_constructible_v<Tj, U> is true, and

    • the expression FUN(std::forward<U>(u)) is well-formed.

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

    Initializes the contained value of type U with the arguments std::forward<A>(a)…​.

    Ensures:

    holds_alternative<U>(*this).

    Throws:

    Any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless there is exactly one occurrence of U in T…​ and std::is_constructible_v<U, A…​> is true.

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

    Initializes the contained value of type U with the arguments il, std::forward<A>(a)…​.

    Ensures:

    holds_alternative<U>(*this).

    Throws:

    Any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless there is exactly one occurrence of U in T…​ and std::is_constructible_v<U, initializer_list<V>&, A…​> is true.

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

    Initializes the contained value of type TI with the arguments std::forward<A>(a)…​.

    Ensures:

    index() == I.

    Throws:

    Any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless I < sizeof…​(T) and std::is_constructible_v<TI, A…​> is true.

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

    Initializes the contained value of type TI with the arguments il, std::forward<A>(a)…​.

    Ensures:

    index() == I.

    Throws:

    Any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless I < sizeof…​(T) and std::is_constructible_v<TI, initializer_list<V>&, A…​> is true.

Destructor
~variant();
  • Effects:

    Destroys the currently contained value.

Assignment
constexpr variant& operator=( const variant& r )
  noexcept( mp_all<std::is_nothrow_copy_constructible<T>...,
    std::is_nothrow_copy_assignable<T>...>::value );
  • Let j be r.index().

    Effects:
    • If index() == j, assigns the value contained in r to the value contained in *this.

    • Otherwise, equivalent to emplace<j>(get<j>(r)).

    Returns:

    *this.

    Ensures:

    index() == r.index().

    Remarks:

    This operator does not participate in overload resolution unless std::is_copy_constructible_v<Ti> && std::is_copy_assignable_v<Ti> is true for all i.

constexpr variant& operator=( variant&& r )
  noexcept( mp_all<std::is_nothrow_move_constructible<T>...,
    std::is_nothrow_move_assignable<T>...>::value );
  • Let j be r.index().

    Effects:
    • If index() == j, assigns the value contained in std::move(r) to the value contained in *this.

    • Otherwise, equivalent to emplace<j>(get<j>(std::move(r))).

    Returns:

    *this.

    Ensures:

    index() == r.index().

    Remarks:

    This operator does not participate in overload resolution unless std::is_move_constructible_v<Ti> && std::is_move_assignable_v<Ti> is true for all i.

template<class U> constexpr variant& operator=( U&& u )
  noexcept( /*see below*/ );
  • Let Tj be a type that is determined as follows: build an imaginary function FUN(Ti) for each alternative type Ti. The overload FUN(Tj) selected by overload resolution for the expression FUN(std::forward<U>(u)) defines the alternative Tj which is the type of the contained value after construction.

    Effects:
    • If index() == j, assigns std::forward<U>(u) to the value contained in *this.

    • Otherwise, equivalent to emplace<j>(std::forward<U>(u)).

    Returns:

    *this.

    Ensures:

    index() == j.

    Remarks:

    The expression inside noexcept is std::is_nothrow_constructible_v<Tj, U> && std::is_nothrow_assignable_v<Tj&, U>. This operator does not participate in overload resolution unless

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

    • std::is_constructible_v<Tj, U> && std::is_assignable_v<Tj&, U> is true, and

    • the expression FUN(std::forward<U>(u)) (with FUN being the above-mentioned set of imaginary functions) is well-formed.

Modifiers
template<class U, class... A>
  constexpr U& emplace( A&&... a );
  • Let I be the zero-based index of U in T…​.

    Effects:

    Equivalent to: return emplace<I>(std::forward<A>(a)…​);

    Remarks:

    This function shall not participate in overload resolution unless std::is_constructible_v<U, A…​> is true and U occurs exactly once in T…​.

template<class U, class V, class... A>
  constexpr U& emplace( std::initializer_list<V> il, A&&... a );
  • Let I be the zero-based index of U in T…​.

    Effects:

    Equivalent to: return emplace<I>(il, std::forward<A>(a)…​);

    Remarks:

    This function shall not participate in overload resolution unless std::is_constructible_v<U, std::initializer_list<V>&, A…​> is true and U occurs exactly once in T…​.

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

    I < sizeof…​(T).

    Effects:

    Destroys the currently contained value, then initializes a new contained value as if using the expression Ti(std::forward<A>(a)…​).

    Ensures:

    index() == I.

    Returns:

    A reference to the new contained value.

    Throws:

    Nothing unless the initialization of the new contained value throws.

    Exception Safety:

    On exception:

    • If the list of alternatives contains monostate, the contained value is either unchanged, or monostate{};

    • Otherwise, if the list of alternatives contains types for which is_nothrow_default_constructible_v is true, the contained value is either unchanged, or Tj{}, where Tj is the first such alternative;

    • Otherwise, the contained value is unchanged.

    Remarks:

    This function shall not participate in overload resolution unless std::is_constructible_v<Ti, A…​> is true.

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

    I < sizeof…​(T).

    Effects:

    Destroys the currently contained value, then initializes a new contained value as if using the expression Ti(il, std::forward<A>(a)…​).

    Ensures:

    index() == I.

    Returns:

    A reference to the new contained value.

    Throws:

    Nothing unless the initialization of the new contained value throws.

    Exception Safety:

    On exception:

    • If the list of alternatives contains monostate, the contained value is either unchanged, or monostate{};

    • Otherwise, if the list of alternatives contains types for which is_nothrow_default_constructible_v is true, the contained value is either unchanged, or Tj{}, where Tj is the first such alternative;

    • Otherwise, the contained value is unchanged.

    Remarks:

    This function shall not participate in overload resolution unless std::is_constructible_v<Ti, std::initializer_list<V>&, A…​> is true.

Value Status
constexpr bool valueless_by_exception() const noexcept;
  • Returns:

    false.

constexpr size_t index() const noexcept;
  • Returns:

    The zero-based index of the active alternative.

Swap
void swap( variant& r ) noexcept( mp_all<std::is_nothrow_move_constructible<T>...,
  is_nothrow_swappable<T>...>::value );
  • Effects:
    • If index() == r.index(), calls swap(get<I>(*this), get<I>(r)), where I is index().

    • Otherwise, as if variant tmp(std::move(*this)); *this = std::move(r); r = std::move(tmp);

Converting Constructors (extension)
template<class... U> variant( variant<U...> const& r )
  noexcept( mp_all<std::is_nothrow_copy_constructible<U>...>::value );
  • Effects:

    Initializes the contained value from the contained value of r.

    Throws:

    Any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless all types in U…​ are in T…​ and std::is_copy_constructible_v<Ui>::value is true for all Ui.

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

    Initializes the contained value from the contained value of std::move(r).

    Throws:

    Any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless all types in U…​ are in T…​ and std::is_move_constructible_v<Ui>::value is true for all Ui.

Subset (extension)
template<class... U> constexpr variant<U...> subset() & ;
template<class... U> constexpr variant<U...> subset() const& ;
  • Returns:

    A variant<U…​> whose contained value is copy-initialized from the contained value of *this and has the same type.

    Throws:
    • If the active alternative of *this is not among the types in U…​, bad_variant_access.

    • Otherwise, any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless all types in U…​ are in T…​ and std::is_copy_constructible_v<Ui>::value is true for all Ui.

template<class... U> constexpr variant<U...> subset() && ;
template<class... U> constexpr variant<U...> subset() const&& ;
  • Returns:

    A variant<U…​> whose contained value is move-initialized from the contained value of *this and has the same type.

    Throws:
    • If the active alternative of *this is not among the types in U…​, bad_variant_access.

    • Otherwise, any exception thrown by the initialization of the contained value.

    Remarks:

    This function does not participate in overload resolution unless all types in U…​ are in T…​ and std::is_move_constructible_v<Ui>::value is true for all Ui.

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
  • If typename variant_alternative<I, T>::type exists and is U,

    • variant_alternative<I, T const>::type is U const;

    • variant_alternative<I, T volatile>::type is U volatile;

    • variant_alternative<I, T const volatile>::type is U const volatile.

    • variant_alternative<I, T&>::type is U&.

    • variant_alternative<I, T&&>::type is U&&.

    Otherwise, these structs have no member type.

template<size_t I, class... T>
  struct variant_alternative<I, variant<T...>>;
  • When I < sizeof…​(T), the nested type type is an alias for the I-th (zero-based) type in T…​. Otherwise, there is no member type.

holds_alternative

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

    The type U occurs exactly once in T…​. Otherwise, the program is ill-formed.

    Returns:

    true if index() is equal to the zero-based index of U in T…​.

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);
  • Effects:

    If v.index() is I, returns a reference to the object stored in the variant. Otherwise, throws bad_variant_access.

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);
  • Requires:

    The type U occurs exactly once in T…​. Otherwise, the program is ill-formed.

    Effects:

    If v holds a value of type U, returns a reference to that value. Otherwise, throws 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;
  • Requires:

    I < sizeof…​(U). Otherwise, the program is ill-formed.

    Effects:

    A pointer to the value stored in the variant, if v != nullptr && v->index() == I. Otherwise, nullptr.

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;
  • Requires:

    The type U occurs exactly once in T…​. Otherwise, the program is ill-formed.

    Effects:

    Equivalent to: return get_if<I>(v); with I being the zero-based index of U in T…​.

Relational Operators

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

    v.index() == w.index && get<I>(v) == get<I>(w), where I is v.index().

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

    !(v == w).

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

    v.index() < w.index || (v.index() == w.index && get<I>(v) < get<I>(w)), where I is v.index().

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

    w < v.

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

    v.index() < w.index || (v.index() == w.index && get<I>(v) <= get<I>(w)), where I is v.index().

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

    w <= v.

visit

template<class F, class... V>
  constexpr /*see below*/ visit(F&& f, V&&... v);
  • Returns:

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

swap

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

    Equivalent to v.swap(w).

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";
    }
};

This documentation is copyright 2018, 2019 Peter Dimov and is distributed under the Boost Software License, Version 1.0.