3#ifndef RPNXDATASTRUCTURES_ANNEX_HPP
4#define RPNXDATASTRUCTURES_ANNEX_HPP
7#include <initializer_list>
30 template <
typename T,
typename Alloc = std::allocator< T > >
33 static_assert(!std::is_reference_v< T >,
"rpnx::annex does not support reference types");
34 static_assert(!std::is_void_v< T >,
"rpnx::annex does not support void");
45 using allocator_type =
typename std::allocator_traits< Alloc >::template rebind_alloc< T >;
48 using alloc_traits = std::allocator_traits< allocator_type >;
53 template <
typename... Args >
54 T* allocate_construct(Args&&... args)
56 T* ptr = alloc_traits::allocate(m_alloc, 1);
60 alloc_traits::construct(m_alloc, ptr, std::forward< Args >(args)...);
64 alloc_traits::deallocate(m_alloc, ptr, 1);
71 void destroy_deallocate() noexcept
73 if (m_value ==
nullptr)
78 alloc_traits::destroy(m_alloc, m_value);
79 alloc_traits::deallocate(m_alloc, m_value, 1);
83 template <
typename U >
84 void assign_value(U&&
value)
86 if (m_value !=
nullptr)
88 **
this = std::forward< U >(
value);
92 m_value = allocate_construct(std::forward< U >(
value));
133 annex(
annex const& other) : m_alloc(alloc_traits::select_on_container_copy_construction(other.m_alloc))
137 m_value = allocate_construct(*other);
148 annex(
annex&& other)
noexcept(std::is_nothrow_move_constructible_v< allocator_type >) : m_value(other.m_value), m_alloc(std::move(other.m_alloc))
150 other.m_value =
nullptr;
158 template <
typename U = T,
typename = std::enable_if_t< std::is_constructible_v< T, U&& > && !std::is_same_v< std::decay_t< U >, annex > && !std::is_same_v< std::decay_t< U >, std::in_place_t > && !std::is_same_v< std::decay_t< U >, std::nullopt_t > && !std::is_same_v< std::decay_t< U >, allocator_type > > >
161 m_value = allocate_construct(std::forward< U >(
value));
169 template <
typename... Args >
170 explicit annex(std::in_place_t, Args&&... args)
172 m_value = allocate_construct(std::forward< Args >(args)...);
182 template <
typename U,
typename... Args >
183 explicit annex(std::in_place_t, std::initializer_list< U > init, Args&&... args)
185 m_value = allocate_construct(init, std::forward< Args >(args)...);
194 template <
typename... Args >
195 annex(std::allocator_arg_t,
allocator_type const& alloc, std::in_place_t, Args&&... args) : m_alloc(alloc)
197 m_value = allocate_construct(std::forward< Args >(args)...);
208 template <
typename U,
typename... Args >
209 annex(std::allocator_arg_t,
allocator_type const& alloc, std::in_place_t, std::initializer_list< U > init, Args&&... args) : m_alloc(alloc)
211 m_value = allocate_construct(init, std::forward< Args >(args)...);
219 destroy_deallocate();
244 if constexpr (alloc_traits::propagate_on_container_copy_assignment::value)
246 if (m_alloc != other.m_alloc)
251 m_alloc = other.m_alloc;
256 assign_value(*other);
276 annex&
operator=(
annex&& other)
noexcept(alloc_traits::propagate_on_container_move_assignment::value && std::is_nothrow_move_assignable_v< allocator_type >)
283 if constexpr (alloc_traits::propagate_on_container_move_assignment::value)
286 m_alloc = std::move(other.m_alloc);
287 m_value = other.m_value;
288 other.m_value =
nullptr;
292 if (m_alloc == other.m_alloc)
295 m_value = other.m_value;
296 other.m_value =
nullptr;
298 else if (other.has_value())
300 assign_value(std::move(*other));
321 template <
typename U = T,
typename = std::enable_if_t< std::is_constructible_v< T, U&& > && std::is_assignable_v< T&, U&& > && !std::is_same_v< std::decay_t< U >, annex > && !std::is_same_v< std::decay_t< U >, std::nullopt_t > > >
324 assign_value(std::forward< U >(
value));
375 return std::move(*m_value);
385 return std::move(*m_value);
392 constexpr explicit operator bool() const noexcept
403 return m_value !=
nullptr;
415 throw std::bad_optional_access();
430 throw std::bad_optional_access();
445 throw std::bad_optional_access();
448 return std::move(**
this);
460 throw std::bad_optional_access();
463 return std::move(**
this);
472 template <
typename U >
475 static_assert(std::is_copy_constructible_v< T >);
476 static_assert(std::is_convertible_v< U&&, T >);
477 return has_value() ? **this :
static_cast< T
>(std::forward< U >(default_value));
486 template <
typename U >
489 static_assert(std::is_move_constructible_v< T >);
490 static_assert(std::is_convertible_v< U&&, T >);
491 return has_value() ? std::move(**
this) :
static_cast< T
>(std::forward< U >(default_value));
498 void swap(
annex& other)
noexcept(alloc_traits::propagate_on_container_swap::value && std::is_nothrow_swappable_v< allocator_type > && std::is_nothrow_swappable_v< T >)
502 if constexpr (alloc_traits::propagate_on_container_swap::value)
504 swap(m_alloc, other.m_alloc);
505 swap(m_value, other.m_value);
507 else if (
has_value() && other.has_value())
509 swap(**
this, *other);
513 other.m_value = other.allocate_construct(std::move(**
this));
516 else if (other.has_value())
518 m_value = allocate_construct(std::move(*other));
528 destroy_deallocate();
537 template <
typename... Args >
541 m_value = allocate_construct(std::forward< Args >(args)...);
553 template <
typename U,
typename... Args >
554 T&
emplace(std::initializer_list< U > init, Args&&... args)
557 m_value = allocate_construct(init, std::forward< Args >(args)...);
576 template <
typename T,
typename Alloc >
597 template <
typename T,
typename Alloc >
614 template <
typename T,
typename Alloc >
617 return !(lhs == rhs);
626 template <
typename T,
typename Alloc >
638 template <
typename T,
typename Alloc >
650 template <
typename T,
typename Alloc >
662 template <
typename T,
typename Alloc >
674 template <
typename T,
typename Alloc >
675 requires requires(T
const& lhs, T
const& rhs) { lhs <=> rhs; }
678 if (lhs.has_value() && rhs.has_value())
680 return *lhs <=> *rhs;
685 return std::strong_ordering::greater;
690 return std::strong_ordering::less;
693 return std::strong_ordering::equal;
711 template <
typename T,
typename Alloc >
714 return !lhs.has_value();
722 template <
typename T,
typename Alloc >
725 return !rhs.has_value();
733 template <
typename T,
typename Alloc >
736 return lhs.has_value();
744 template <
typename T,
typename Alloc >
747 return rhs.has_value();
754 template <
typename T,
typename Alloc >
765 template <
typename T,
typename Alloc >
768 return rhs.has_value();
776 template <
typename T,
typename Alloc >
779 return !lhs.has_value();
786 template <
typename T,
typename Alloc >
797 template <
typename T,
typename Alloc >
800 return lhs.has_value();
807 template <
typename T,
typename Alloc >
817 template <
typename T,
typename Alloc >
828 template <
typename T,
typename Alloc >
831 return !rhs.has_value();
839 template <
typename T,
typename Alloc >
842 return lhs.has_value() ? std::strong_ordering::greater : std::strong_ordering::equal;
850 template <
typename T,
typename Alloc >
853 return rhs.has_value() ? std::strong_ordering::less : std::strong_ordering::equal;
872 template <
typename T,
typename Alloc,
typename U >
873 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
876 return lhs.
has_value() ? *lhs == rhs :
false;
885 template <
typename T,
typename Alloc,
typename U >
886 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
889 return rhs.
has_value() ? lhs == *rhs :
false;
898 template <
typename T,
typename Alloc,
typename U >
899 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
902 return !(lhs == rhs);
911 template <
typename T,
typename Alloc,
typename U >
912 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
915 return !(lhs == rhs);
924 template <
typename T,
typename Alloc,
typename U >
925 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
928 return lhs.
has_value() ? *lhs < rhs :
true;
937 template <
typename T,
typename Alloc,
typename U >
938 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
941 return rhs.
has_value() ? lhs < *rhs :
false;
950 template <
typename T,
typename Alloc,
typename U >
951 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
954 return lhs.
has_value() ? *lhs <= rhs :
true;
963 template <
typename T,
typename Alloc,
typename U >
964 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
967 return rhs.
has_value() ? lhs <= *rhs :
false;
976 template <
typename T,
typename Alloc,
typename U >
977 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
980 return lhs.
has_value() ? *lhs > rhs :
false;
989 template <
typename T,
typename Alloc,
typename U >
990 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
993 return rhs.
has_value() ? lhs > *rhs :
true;
1002 template <
typename T,
typename Alloc,
typename U >
1003 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
1006 return lhs.
has_value() ? *lhs >= rhs :
false;
1015 template <
typename T,
typename Alloc,
typename U >
1016 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >)
1019 return rhs.
has_value() ? lhs >= *rhs :
true;
1028 template <
typename T,
typename Alloc,
typename U >
1029 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >) &&
requires(T
const& lhs, U
const& rhs) { lhs <=> rhs; }
1032 if (lhs.has_value())
1034 return *lhs <=> rhs;
1037 return std::strong_ordering::less;
1046 template <
typename T,
typename Alloc,
typename U >
1047 requires(!
requires {
typename U::value_type;
typename U::allocator_type; } || !std::is_same_v< U, annex< typename U::value_type, typename U::allocator_type > >) &&
requires(U
const& lhs, T
const& rhs) { lhs <=> rhs; }
1050 if (rhs.has_value())
1052 return lhs <=> *rhs;
1055 return std::strong_ordering::greater;
1066 template <
typename T >
1079 template <
typename T,
typename... Args >
1082 return annex< T >(std::in_place, std::forward< Args >(args)...);
1094 template <
typename T,
typename U,
typename... Args >
1097 return annex< T >(std::in_place, init, std::forward< Args >(args)...);
Optional-like value container that stores its payload out of line.
Definition annex.hpp:32
constexpr T && operator*() &&noexcept
Returns an rvalue reference to the contained value.
Definition annex.hpp:383
T && value() &&
Returns the contained value as an rvalue reference.
Definition annex.hpp:441
annex(allocator_type const &alloc) noexcept
Constructs an empty annex using the specified allocator.
Definition annex.hpp:113
allocator_type get_allocator() const
Returns a copy of the allocator.
Definition annex.hpp:565
typename std::allocator_traits< Alloc >::template rebind_alloc< T > allocator_type
Allocator rebound to T.
Definition annex.hpp:45
annex & operator=(annex &&other) noexcept(alloc_traits::propagate_on_container_move_assignment::value &&std::is_nothrow_move_assignable_v< allocator_type >)
Move-assigns another annex.
Definition annex.hpp:276
annex(std::allocator_arg_t, allocator_type const &alloc, std::in_place_t, Args &&... args)
Constructs the contained value in place using the specified allocator.
Definition annex.hpp:195
annex & operator=(std::nullopt_t) noexcept
Assigns the empty state.
Definition annex.hpp:226
constexpr T value_or(U &&default_value) &&
Returns the contained value or a fallback.
Definition annex.hpp:487
constexpr T const & operator*() const &noexcept
Returns a const lvalue reference to the contained value.
Definition annex.hpp:353
void reset() noexcept
Destroys the contained value and makes the annex empty.
Definition annex.hpp:526
T & emplace(Args &&... args)
Replaces the contained value by constructing a new value in place.
Definition annex.hpp:538
constexpr T const * operator->() const noexcept
Returns a pointer to the contained value.
Definition annex.hpp:333
annex(std::allocator_arg_t, allocator_type const &alloc) noexcept
Constructs an empty annex using allocator-argument syntax.
Definition annex.hpp:121
annex & operator=(U &&value)
Assigns a value to the annex.
Definition annex.hpp:322
constexpr T const && operator*() const &&noexcept
Returns a const rvalue reference to the contained value.
Definition annex.hpp:373
~annex()
Destroys the contained value, if any, and releases its storage.
Definition annex.hpp:217
annex(std::in_place_t, Args &&... args)
Constructs the contained value in place.
Definition annex.hpp:170
constexpr bool has_value() const noexcept
Tests whether the annex contains a value.
Definition annex.hpp:401
annex(std::in_place_t, std::initializer_list< U > init, Args &&... args)
Constructs the contained value in place with an initializer list.
Definition annex.hpp:183
T const & value() const &
Returns the contained value.
Definition annex.hpp:426
annex & operator=(annex const &other)
Copy-assigns another annex.
Definition annex.hpp:237
constexpr T value_or(U &&default_value) const &
Returns the contained value or a fallback.
Definition annex.hpp:473
T & value() &
Returns the contained value.
Definition annex.hpp:411
annex(annex &&other) noexcept(std::is_nothrow_move_constructible_v< allocator_type >)
Move-constructs an annex by transferring its backing pointer.
Definition annex.hpp:148
constexpr T * operator->() noexcept
Returns a pointer to the contained value.
Definition annex.hpp:343
annex(U &&value)
Constructs an annex containing a value.
Definition annex.hpp:159
T & emplace(std::initializer_list< U > init, Args &&... args)
Replaces the contained value using an initializer list.
Definition annex.hpp:554
T value_type
The contained value type.
Definition annex.hpp:40
constexpr annex() noexcept(std::is_nothrow_default_constructible_v< allocator_type >)=default
Constructs an empty annex.
constexpr T & operator*() &noexcept
Returns an lvalue reference to the contained value.
Definition annex.hpp:363
annex(std::allocator_arg_t, allocator_type const &alloc, std::in_place_t, std::initializer_list< U > init, Args &&... args)
Constructs the contained value in place with an initializer list and allocator.
Definition annex.hpp:209
T const && value() const &&
Returns the contained value as a const rvalue reference.
Definition annex.hpp:456
void swap(annex &other) noexcept(alloc_traits::propagate_on_container_swap::value &&std::is_nothrow_swappable_v< allocator_type > &&std::is_nothrow_swappable_v< T >)
Swaps this annex with another annex.
Definition annex.hpp:498
annex(annex const &other)
Copy-constructs an annex.
Definition annex.hpp:133
Containers, iterator adapters, callable wrappers, and value utilities.
Definition annex.hpp:14
void swap(annex< T, Alloc > &lhs, annex< T, Alloc > &rhs) noexcept(noexcept(lhs.swap(rhs)))
Swaps two annex objects.
Definition annex.hpp:577
bool operator<=(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs)
Orders two annex objects.
Definition annex.hpp:651
bool operator>=(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs)
Orders two annex objects.
Definition annex.hpp:663
annex< std::decay_t< T > > make_annex(T &&value)
Constructs an annex containing a decayed copy of a value.
Definition annex.hpp:1067
bool operator<(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs)
Orders two annex objects.
Definition annex.hpp:627
bool operator!=(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs)
Compares two annex objects for inequality.
Definition annex.hpp:615
bool operator>(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs)
Orders two annex objects.
Definition annex.hpp:639
auto operator<=>(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs) -> decltype(*lhs<=> *rhs)
Three-way compares two annex objects when T supports <=>.
Definition annex.hpp:676
bool operator==(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs)
Compares two annex objects for equality.
Definition annex.hpp:598