Overview

This library implements a type-safe discriminated union (variant) type, variant<T…​>, that almost conforms to the C++17 Standard’s std::variant<T…​>. The main differences between the two are:

  • variant<T…​> does not have a valueless-by-exception state;

  • A converting constructor from, e.g. variant<int, float> to variant<float, double, int> is provided as an extension;

  • The reverse operation, going from variant<float, double, int> to variant<int, float> is provided as the member function subset<U…​>. (This operation can throw if the current state of the variant cannot be represented.)

  • variant<T…​> is not trivial when all contained types are trivial, as mandated by C++17.

To avoid the valueless-by-exception state, this implementation falls back to using double storage unless

  • one of the alternatives is the type monostate,

  • one of the alternatives has a nonthrowing default constructor, or

  • all the contained types are nothrow move constructible.

If the first two bullets don’t hold, but the third does, the variant uses single storage, but emplace constructs a temporary and moves it into place if the construction of the object can throw. In case this is undesirable, one can force emplace into always constructing in-place by adding monostate as one of the alternatives.

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.

    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.

    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.