PrevUpHomeNext

Class template variant

A type-safe discriminated union container.

Synopsis

Declared in file <strict_variant/variant_fwd.hpp>.

Defined in file <strict_variant/variant.hpp>:

namespace strict_variant {

  template <typename T>
  struct emplace_tag {};

  template <typename First, typename... Types>
  class variant {

    // Default constructs `First()`.
    variant();

    // Special member functions
    variant(const variant &);
    variant(variant &&);
    ~variant() noexcept;

    // Only available if all input types are no-throw move constructible
    variant & operator=(variant &&);
    variant & operator=(const variant &);

    // Forwarding-reference ctor: Places the given value in the variant.
    template <typename T>
    variant(T &&);

    // Constructs the variant from a "subvariant"
    template <typename... OTypes>
    variant(const variant<Otypes...> &);

    template <typename... OTypes>
    variant(variant<Otypes...> &&);

    // Emplace ctor
    template <typename T, typename... Args>
    explicit variant(emplace_tag<T>, Args && ... args);

    // Emplace operation
    template <typename T, typename... Args>
    void emplace(Args &&... args);

    template <std::size_t, typename... Args>
    void emplace(Args &&... args);

    // Standard swap
    void swap(variant &) noexcept;

    // Reports the runtime type.
    int which() const;

    // Access the contained value
    template <typename T>
    T * get();

    template <typename T>
    const T * get() const;

    template <std::size_t index>
    auto * get();

    template <std::size_t index>
    const auto * get() const;

  };

  template <typename... Types>
  inline bool operator==(const variant<Types...> &, const variant<Types...> &);

  template <typename... Types>
  inline bool operator!=(const variant<Types...> &, const variant<Types...> &);

  template <typename... Types>
  void swap(variant<Types...> &, variant<Types...> &) noexcept;

  template <typename T, typename ... Types>
  T * get(variant<Types...> * v);

  template <typename T, typename ... Types>
  const T * get(const variant<Types...> * v);

  template <typename T, typename ... Types>
  T & get_or_default(variant<Types...> & v, T def = {});

  template <typename Visitor, typename Variant>
  void apply_visitor(Visitor && visitor, Variant && var);

  template <typename... Types>
  using easy_variant;

} // end namespace strict_variant

Description

variant is the core object of the strict_variant library. It controls the storage in which the value is contained, and provides an interface for accessing the value and changing the value.

Type Requirements

The template parameters First, Types... are called the value types of the variant. These are the different possible types that the contained value may have.

Each value type:

Must be nothrow_destructible
This is enforced by static_assert.
Must not be a reference type.

This is enforced by static_assert. Use std::reference_wrapper if you need this.

Must be complete at point of instantiation of the variant.

Use recursive_wrapper or similar to work around this.

Notes

The strict_variant::variant may be used similarly to boost::variant.

(However, this interface is exception-free. If you want to have analogues of the throwing functions in boost::variant you'll have to write them, which is pretty easy to do on top of the exception-free interface.)

The properties of the value types determine the properties of the resulting variant.

Other properties of variant:

In all cases, all operations on this variant are strongly exception-safe and provide rollback-semantics if an exception is thrown while changing the type of the contained value.

Member Functions

Constructors

variant()

Attempts to default construct the First type.

Requires

First is DefaultConstructible.

Throws

If First() does.

variant(const variant &)

Copy constructs a variant.

Requires

Each value type is CopyConstructible.

Throws

If any of the copy constructors does.

variant(variant &&)

Move constructs a variant.

Requires

Each value type is MoveConstructible.

Throws
  • If any of the moves throws
  • If any type is recursive_wrapper, may throw std::bad_alloc.
  • If any type is recursive_wrapper<T>, and T has a throwing move, then this may throw such exceptions.
variant(const variant<OFirst, OTypes...> &)

Generalizing constructor.

Copy constructs a variant from another variant over a subset of the value types.

Requires

Each type OFirst, OTypes... is also a value type of this variant, modulo const and recursive_wrapper.

Throws

If the copy constructor of the passed variant may throw.

variant(variant<OFirst, OTypes...> &&)

Generalizing constructor.

Move constructs a variant from another variant over a subset of the value types.

Requires

Each type OFirst, OTypes... is also a value type of this variant, modulo const and recursive_wrapper.

Throws

If the move constructor of the passed variant may throw.

variant(T &&)

Constructs from a value convertible (via a permitted conversion) to one of the value types.

Attempts to convert T to one of the value types of this variant, using C++ overload resolution, with each of the value types as a candidate.

However, before overload resolution occurs, some candidates are eliminated, if for instance they represent a non-portable conversion.

Candidates are eliminated in two phases:

  • First, types which are not safely constructible from T are elminated.
  • Second, types which are dominated by one of the remaining types are eliminated.

Overload resolution occurs with the remaining types.

Unless you customize those type traits, candidates are only eliminated when both:

  • The argument is a built-in arithmetic type or a pointer type.
  • The value type is a built-in arithmetic type or a pointer type.

This feature acts to prevent:

  • Conversions which could be narrowing on some conforming implementation of C++.
  • Implicit conversions to / from bool, pointers, and others.
  • Certain pointer conversions.
  • Certain kinds of overload ambiguity when there are many integral types in the variant.

Here are some concrete annotated examples:

variant<short, long> v{10};                   // Selects `long`, `short` is not safely constructible from `int`.
variant<int, long> v{10};                     // Selects `int`, it is lower rank. (`int` dominates `long`.)
variant<long, long long> v{10};               // Selects `long`, it is lower rank. (dominates)
variant<int, unsigned int> v{10};             // Selects `int`, it has a better conversion (identity)
variant<long, unsigned int> v{10};            // Neither dominates.
variant<long long, unsigned long> v{10};      // Neither dominates.
variant<unsigned int, unsigned long> v{10u};  // Selects `unsigned int`, it is lower rank.
variant<long, unsigned long long> v{10u};     // `unsigned -> signed long` is considered unsafe.

See relevant type traits for more detail: safely_constructible and dominates

variant(emplace_tag<T>, Args && ... args)

Emplace-constructs a variant.

Constructs a value of type T in the storage. Any additional arguments args are forwarded to T's constructor.

Requires

T is one of the value types of this variant, modulo const and recursive_wrapper.

Throws

If the selected constructor may throw.

Assignment

variant & operator=(const variant &)

Copy-assigns a variant.

Requires

Each value type is Assignable, CopyConstructible, and nothrow_move_constructible.

Throws

If copy construction or copy assignment throws.

variant & operator=(variant &&)

Move-assigns a variant.

Requires

Each value type is MoveAssignable and nothrow_move_constructible.

Throws
  • If the type has a throwing move assignment, then this may throw such exceptions.
  • If the engaged type is recursive_wrapper<T>, then this may throw std::bad_alloc.
  • If the engaged type is recursive_wrapper<T>, and T has a throwing move, then this may throw such exceptions.
  • Otherwise this call is noexcept.
variant & operator=(const variant<OFirst, OTypes...> &)

Copy-assigns from a variant over a subset of the value types.

Requires

Has the same requirements and exception scenarios as the corresponding "Generalizing" copy ctor.

variant & operator=(variant<OFirst, OTypes...> &&)

Move-assigns from a variant over a subset of the value types.

Requires

Has the same requirements and exception scenarios as the corresponding "Generalizing" move ctor.

variant & operator=(T &&)

Assigns from a value convertible (via a permitted conversion) to one of the value types.

Requires

Has the same requirements and exception scenarios as the corresponding converting ctor.

template <typename T, typename ... Args> void emplace(Args && ... args)

Emplaces a value of type T into the variant.

Force the variant to a particular type and value.

The user explicitly specifies the desired type as a template parameter.

There are actually two implementations of emplace, depending on whether the invoked ctor is noexcept. If it is, construction takes in place directly in the storage. If it is not, construction takes place on the stack, and the value is moved into storage. (For strong exception-safety.)

emplace may be used to resolve ambiguity in assignment, or to put a value in the container which is not nothrow_move_constructible.

Requires

T must be a value type of the variant, modulo const and recursive_wrapper, and either the invoked constructor, or the move constructor of T, must be noexcept.

Throws

Only if the invoked constructor throws.

template <std::size_t i, typename ... Args> void emplace(Args && ... args)

Emplaces a value of the i'th value type into the variant.

Force the variant to a particular type and value.

Throws

Only if the invoked constructor throws.

void swap(variant &) noexcept

Standard swap implementation.

Requires

Each value type is nothrow_move_constructible.

Destructor

~variant() noexcept

Destroys the contained value. Never throws.

Accessors

int which() const noexcept

Returns the which indicator value.

The which value is an index into the list First, Types... of value types, indicating the currently contained type.

template <typename T> T * get() noexcept
template <typename T> const T * get() const noexcept

Returns a pointer to the current value if T is the type of the currently contained value. (Ignoring const qualifier and recursive_wrapper.) Returns nullptr otherwise.

template <std::size_t index> auto * get() noexcept
template <std::size_t index> const auto * get() const noexcept

Returns a pointer to the current value if which() == index. Returns nullptr otherwise.

(Unwraps any recursive_wrapper.)

Template Functions

template <typename... Types> inline bool operator ==(const variant<Types...> & first, const variant<Types...> & second) const

Checks if first.which() == second.which(). Then checks if the contained values compare equal using operator ==.

Requires

Each value type is EqualityComparable.

Throws

If equality-comparison can throw.

template <typename... Types> inline bool operator !=(const variant<Types...> & first, const variant<Types...> & second) const

Returns the negation of operator ==.

template <typename... Types> void swap(variant<Types...> & first, variant<Types...> & other) noexcept

Swap the values contained in two containers.

Requires

Each value type is nothrow_move_constructible.

template <typename T> T * get(variant *)
template <typename T> const T * get(const variant *)
template <std::size_t index> auto * get(variant *)
template <std::size_t index> const auto * get(const variant *)

Equivalent to variant::get member function.

template <typename T> T & get_or_default(variant * v, T def = {})

Returns a reference to the stored value. If it does not currently have the indicated type, then the argument def is emplaced into the variant, and a reference to that value, within the variant, is returned.

Requires

T is one of the value types of the variant, modulo const and recursive_wrapper.

Throws

Only if v->emplace<T>(std::move(def)) throws.

template <typename Visitor, typename Variant> auto apply_visitor(Visitor && visitor, Variant && variant)

Applies visitor to the value contained in variant.

Returns

Result of call visitor(value) where value is the value contained by variant, with same value-category as variant.

Requires
  • The call expression visitor(value) is well-formed for each value type of the variant when visitor and value are forwarded with appropriate value-category
  • Each of these expressions evaluate to the same type, or a common type can be found via e.g. integer promotion or a common base class.
Throws
If the call expression throws.
[Note] Note

The mechanism for finding a common return type is similar to std::common_type of C++14, however, we have modified it so that it does not decay lvalue reference types. This is as a workaround to Library Working Group Defect #2141. Other return types will be subject to std::decay.

template <typename Visitor, typename... Variants> auto apply_visitor(Visitor && visitor, Variant && ... variants)

Applies visitor simultaneously to each of the values contained in variants.

This call is equivalent to visitor(value1, value2, ...).

This extended form is called multivisitation.

To use it, you must include an extra header <strict_variant/multivisit.hpp>.


PrevUpHomeNext