A type-safe discriminated union container.
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
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.
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:
nothrow_destructible
static_assert
.
This is enforced by static_assert
.
Use std::reference_wrapper
if you need this.
variant
.
Use recursive_wrapper
or similar to work around this.
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.
First
type is
DefaultConstructible
,
then variant
is also.
Otherwise it isn't.
CopyConstructible
,
then variant
is CopyConstructible
.
MoveConstructible
,
then variant
is MoveConstructible
.
Assignable
and CopyConstructible
and nothrow_move_constructible
,
then variant
is Assignable
.
MoveAssignable
and nothrow_move_constructible
,
then variant
is MoveAssignable
.
Other properties of variant
:
EqualityComparable
,
then variant
is EqualityComparable
.
LessThanComparable
,
then a comparator object, VariantComparator
,
suitable for std::map
or std::set
,
may be obtained from #include
<strict_variant/variant_compare.hpp>
.
OutputStreamable
,
then variant
is OutputStreamable
, if the header #include <strict_variant/variant_stream_ops.hpp>
is included.
Hashable
,
then variant
is Hashable
, if the header #include <strict_variant/variant_hash.hpp>
is included.
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.
Constructors
variant()
Attempts to default construct the First
type.
First
is DefaultConstructible
.
If First()
does.
variant(const variant
&)
Copy constructs a variant.
Each value type is CopyConstructible
.
If any of the copy constructors does.
variant(variant &&)
Move constructs a variant.
Each value type is MoveConstructible
.
recursive_wrapper
,
may throw std::bad_alloc
.
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.
Each type OFirst, OTypes...
is also a value type of this
variant
, modulo
const
and recursive_wrapper
.
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.
Each type OFirst, OTypes...
is also a value type of this
variant
, modulo
const
and recursive_wrapper
.
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:
T
are elminated.
Overload resolution occurs with the remaining types.
Unless you customize those type traits, candidates are only eliminated when both:
arithmetic
type or a pointer type.
arithmetic
type or a pointer type.
This feature acts to prevent:
bool
,
pointers, and others.
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.
T
is one of the
value types of this variant
,
modulo const
and
recursive_wrapper
.
If the selected constructor may throw.
Assignment
variant &
operator=(const variant
&)
Copy-assigns a variant.
Each value type is Assignable
,
CopyConstructible
,
and nothrow_move_constructible
.
If copy construction or copy assignment throws.
variant &
operator=(variant &&)
Move-assigns a variant.
Each value type is MoveAssignable
and nothrow_move_constructible
.
recursive_wrapper<T>
, then this may throw
std::bad_alloc
.
recursive_wrapper<T>
, and T
has a throwing move, then this may throw such exceptions.
noexcept
.
variant &
operator=(const variant<OFirst, OTypes...> &)
Copy-assigns from a variant over a subset of the value types.
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.
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.
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
.
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
.
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.
Only if the invoked constructor throws.
void swap(variant &) noexcept
Standard swap
implementation.
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 <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
==
.
Each value type is EqualityComparable
.
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.
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.
T
is one of the
value types of the variant, modulo const
and recursive_wrapper
.
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
.
Result of call visitor(value)
where value is the value contained
by variant
, with
same value-category as variant
.
visitor(value)
is well-formed for each
value type of the variant
when visitor
and value
are forwarded with appropriate value-category
Note | |
---|---|
The mechanism for finding a common return type is similar to |
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>
.