diff --git a/include/beman/optional26/optional.hpp b/include/beman/optional26/optional.hpp index 60baf418..4d433d83 100644 --- a/include/beman/optional26/optional.hpp +++ b/include/beman/optional26/optional.hpp @@ -201,12 +201,16 @@ concept enable_forward_value = !std::is_same_v, optional> && !std::is_same_v, in_place_t> && std::is_constructible_v; template -concept enable_from_other = - !std::is_same_v && std::is_constructible_v && !std::is_constructible_v&> && - !std::is_constructible_v&&> && !std::is_constructible_v&> && - !std::is_constructible_v&&> && !std::is_convertible_v&, T> && - !std::is_convertible_v&&, T> && !std::is_convertible_v&, T> && - !std::is_convertible_v&&, T>; +concept enable_from_other = !std::is_same_v && // + std::is_constructible_v && // + !std::is_constructible_v&> && // + !std::is_constructible_v&&> && // + !std::is_constructible_v&> && // + !std::is_constructible_v&&> && // + !std::is_convertible_v&, T> && // + !std::is_convertible_v&&, T> && // + !std::is_convertible_v&, T> && // + !std::is_convertible_v&&, T>; template concept enable_assign_forward = !std::is_same_v, std::decay_t> && @@ -279,12 +283,12 @@ class optional { requires(!std::is_reference_v && detail::enable_from_other); template - constexpr explicit(!std::is_convertible_v) optional(optional&& rhs) - requires(!std::is_reference_v && detail::enable_from_other); + constexpr explicit(!std::is_convertible_v) optional(const optional& rhs) + requires(std::is_reference_v && detail::enable_from_other); template - constexpr explicit(!std::is_convertible_v) optional(const optional& rhs) - requires(detail::enable_from_other); + constexpr explicit(!std::is_convertible_v) optional(optional&& rhs) + requires(!std::is_reference_v && detail::enable_from_other); // \ref{optional.dtor}, destructor constexpr ~optional() @@ -324,12 +328,12 @@ class optional { requires(!std::is_reference_v && detail::enable_assign_from_other); template - constexpr optional& operator=(optional&& rhs) - requires(!std::is_reference_v && detail::enable_assign_from_other); + constexpr optional& operator=(const optional& rhs) + requires(std::is_reference_v && detail::enable_assign_from_other); template - constexpr optional& operator=(const optional& rhs) - requires(detail::enable_assign_from_other); + constexpr optional& operator=(optional&& rhs) + requires(!std::is_reference_v && detail::enable_assign_from_other); template constexpr T& emplace(Args&&... args); @@ -358,9 +362,9 @@ class optional { constexpr T& value() &; constexpr const T& value() const&; constexpr T&& value() &&; - template + template > constexpr T value_or(U&& u) const&; - template + template > constexpr T value_or(U&& u) &&; // \ref{optional.monadic}, monadic operations @@ -456,7 +460,7 @@ inline constexpr optional::optional(in_place_t, std::initializer_list il, template template inline constexpr optional::optional(U&& u) - requires detail::enable_forward_value //&& std::is_convertible_v + requires detail::enable_forward_value : optional(in_place, std::forward(u)) {} /// Converting copy constructor. @@ -470,24 +474,25 @@ inline constexpr optional::optional(const optional& rhs) } } -/// Converting move constructor. +/// Converting copy constructor for U& template template -inline constexpr optional::optional(optional&& rhs) - requires(!std::is_reference_v && detail::enable_from_other) +inline constexpr optional::optional(const optional& rhs) + requires(std::is_reference_v && detail::enable_from_other) { if (rhs.has_value()) { - construct(std::move(*rhs)); + construct(*rhs); } } +/// Converting move constructor. template template -inline constexpr optional::optional(const optional& rhs) - requires(detail::enable_from_other) +inline constexpr optional::optional(optional&& rhs) + requires(!std::is_reference_v && detail::enable_from_other) { if (rhs.has_value()) { - construct(*rhs); + construct(std::move(*rhs)); } } @@ -578,45 +583,45 @@ inline constexpr optional& optional::operator=(const optional& rhs) return *this; } -/// Converting move assignment operator. -/// -/// Moves the value from `rhs` if there is one. Otherwise resets the stored -/// value in `*this`. template template -inline constexpr optional& optional::operator=(optional&& rhs) - requires(!std::is_reference_v && detail::enable_assign_from_other) +inline constexpr optional& optional::operator=(const optional& rhs) + requires(std::is_reference_v && detail::enable_assign_from_other) { if (has_value()) { if (rhs.has_value()) { - value_ = std::move(*rhs); + value_ = *rhs; } else { hard_reset(); } } else if (rhs.has_value()) { - construct(std::move(*rhs)); + construct(*rhs); } return *this; } +/// Converting move assignment operator. +/// +/// Moves the value from `rhs` if there is one. Otherwise resets the stored +/// value in `*this`. template template -inline constexpr optional& optional::operator=(const optional& rhs) - requires(detail::enable_assign_from_other) +inline constexpr optional& optional::operator=(optional&& rhs) + requires(!std::is_reference_v && detail::enable_assign_from_other) { if (has_value()) { if (rhs.has_value()) { - value_ = *rhs; + value_ = std::move(*rhs); } else { hard_reset(); } } else if (rhs.has_value()) { - construct(*rhs); + construct(std::move(*rhs)); } return *this; diff --git a/papers/P2988/base-optional.tex b/papers/P2988/base-optional.tex index 2871fd7f..613c2689 100644 --- a/papers/P2988/base-optional.tex +++ b/papers/P2988/base-optional.tex @@ -1,5 +1,3 @@ -\rSec0[utilities]{General utilities library} - \rSec1[optional]{Optional objects} \rSec2[optional.general]{General} @@ -88,7 +86,7 @@ constexpr void swap(optional&, optional&) noexcept(@\seebelow@); template - constexpr optional<@\seebelow@> make_optional(T&&); + constexpr optional> make_optional(T&&); template constexpr optional make_optional(Args&&... args); template @@ -124,7 +122,7 @@ constexpr explicit optional(in_place_t, Args&&...); template constexpr explicit optional(in_place_t, initializer_list, Args&&...); - template + template> constexpr explicit(@\seebelow@) optional(U&&); template constexpr explicit(@\seebelow@) optional(const optional&); @@ -138,7 +136,7 @@ constexpr optional& operator=(nullopt_t) noexcept; constexpr optional& operator=(const optional&); constexpr optional& operator=(optional&&) noexcept(@\seebelow@); - template constexpr optional& operator=(U&&); + template> constexpr optional& operator=(U&&); template constexpr optional& operator=(const optional&); template constexpr optional& operator=(optional&&); template constexpr T& emplace(Args&&...); @@ -166,8 +164,8 @@ constexpr T& value() &; // freestanding-deleted constexpr T&& value() &&; // freestanding-deleted constexpr const T&& value() const &&; // freestanding-deleted - template constexpr T value_or(U&&) const &; - template constexpr T value_or(U&&) &&; + template> constexpr T value_or(U&&) const &; + template> constexpr T value_or(U&&) &&; // \ref{optional.monadic}, monadic operations template constexpr auto and_then(F&& f) &; @@ -185,7 +183,7 @@ constexpr void reset() noexcept; private: - T *val; // \expos + T* val; // \expos }; template @@ -197,8 +195,7 @@ Any instance of \tcode{optional} at any given time either contains a value or does not contain a value. When an instance of \tcode{optional} \defnx{contains a value}{contains a value!\idxcode{optional}}, it means that an object of type \tcode{T}, referred to as the optional object's \defnx{contained value}{contained value!\idxcode{optional}}, -is allocated within the storage of the optional object. -Implementations are not permitted to use additional storage, such as dynamic memory, to allocate its contained value. +is nested within\iref{intro.object} the optional object. When an object of type \tcode{optional} is contextually converted to \tcode{bool}, the conversion returns \tcode{true} if the object contains a value; otherwise the conversion returns \tcode{false}. @@ -359,7 +356,7 @@ \indexlibraryctor{optional}% \begin{itemdecl} -template constexpr explicit(@\seebelow@) optional(U&& v); +template> constexpr explicit(@\seebelow@) optional(U&& v); \end{itemdecl} \begin{itemdescr} @@ -611,16 +608,18 @@ \indexlibrarymember{operator=}{optional}% \begin{itemdecl} -template constexpr optional& operator=(U&& v); +template> constexpr optional& operator=(U&& v); \end{itemdecl} \begin{itemdescr} \pnum \constraints -\tcode{is_same_v, optional>} is \tcode{false}, -\tcode{conjunction_v, is_same>>} is \tcode{false}, -\tcode{is_constructible_v} is \tcode{true}, and -\tcode{is_assignable_v} is \tcode{true}. +\begin{itemize} +\item \tcode{is_same_v, optional>} is \tcode{false}, +\item \tcode{conjunction_v, is_same>>} is \tcode{false}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{is_assignable_v} is \tcode{true}. +\end{itemize} \pnum \effects @@ -1046,7 +1045,7 @@ \indexlibrarymember{value_or}{optional}% \begin{itemdecl} -template constexpr T value_or(U&& v) const &; +template> constexpr T value_or(U&& v) const &; \end{itemdecl} \begin{itemdescr} @@ -1064,7 +1063,7 @@ \indexlibrarymember{value_or}{optional}% \begin{itemdecl} -template constexpr T value_or(U&& v) &&; +template> constexpr T value_or(U&& v) &&; \end{itemdecl} \begin{itemdescr} @@ -1512,6 +1511,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{U} is not a specialization of \tcode{optional}. The expression \tcode{*x == v} is well-formed and its result is convertible to \tcode{bool}. \begin{note} @@ -1531,6 +1531,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{T} is not a specialization of \tcode{optional}. The expression \tcode{v == *x} is well-formed and its result is convertible to \tcode{bool}. @@ -1547,6 +1548,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{U} is not a specialization of \tcode{optional}. The expression \tcode{*x != v} is well-formed and its result is convertible to \tcode{bool}. @@ -1563,6 +1565,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{T} is not a specialization of \tcode{optional}. The expression \tcode{v != *x} is well-formed and its result is convertible to \tcode{bool}. @@ -1579,6 +1582,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{U} is not a specialization of \tcode{optional}. The expression \tcode{*x < v} is well-formed and its result is convertible to \tcode{bool}. @@ -1595,6 +1599,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{T} is not a specialization of \tcode{optional}. The expression \tcode{v < *x} is well-formed and its result is convertible to \tcode{bool}. @@ -1611,6 +1616,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{U} is not a specialization of \tcode{optional}. The expression \tcode{*x > v} is well-formed and its result is convertible to \tcode{bool}. @@ -1627,6 +1633,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{T} is not a specialization of \tcode{optional}. The expression \tcode{v > *x} is well-formed and its result is convertible to \tcode{bool}. @@ -1643,6 +1650,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{U} is not a specialization of \tcode{optional}. The expression \tcode{*x <= v} is well-formed and its result is convertible to \tcode{bool}. @@ -1659,6 +1667,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{T} is not a specialization of \tcode{optional}. The expression \tcode{v <= *x} is well-formed and its result is convertible to \tcode{bool}. @@ -1675,6 +1684,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{U} is not a specialization of \tcode{optional}. The expression \tcode{*x >= v} is well-formed and its result is convertible to \tcode{bool}. @@ -1691,6 +1701,7 @@ \begin{itemdescr} \pnum \constraints +\tcode{T} is not a specialization of \tcode{optional}. The expression \tcode{v >= *x} is well-formed and its result is convertible to \tcode{bool}. diff --git a/papers/P2988/mybiblio.bib b/papers/P2988/mybiblio.bib index aeeb9fb3..bbe93d90 100644 --- a/papers/P2988/mybiblio.bib +++ b/papers/P2988/mybiblio.bib @@ -58,3 +58,12 @@ @misc{rawgithu58:online year = {}, note = {(Accessed on 08/14/2024)} } + +@misc{The_Beman_Project_beman_optional26, +author = {The Beman Project}, +license = {Apache-2.0}, +title = {{beman.optional26}}, +howpublished = {\url{https://github.com/bemanproject/optional26}}, +month = {}, +year = {}, +} diff --git a/papers/P2988/new-optional.tex b/papers/P2988/new-optional.tex index 975d43bc..e04f423c 100644 --- a/papers/P2988/new-optional.tex +++ b/papers/P2988/new-optional.tex @@ -92,7 +92,7 @@ constexpr void swap(optional&, optional&) noexcept(@\seebelow@); template - constexpr optional<@\seebelow@> make_optional(T&&); + constexpr optional> make_optional(T&&); template constexpr optional make_optional(Args&&... args); template @@ -129,10 +129,18 @@ constexpr explicit optional(in_place_t, Args&&...); template constexpr explicit optional(in_place_t, initializer_list, Args&&...); - template + template> constexpr explicit(@\seebelow@) optional(U&&); template constexpr explicit(@\seebelow@) optional(const optional&); +\end{codeblock} +\color{addclr} +\begin{codeblock} + template + constexpr explicit(@\seebelow@) optional(const optional&); +\end{codeblock} +\color{black} +\begin{codeblock} template constexpr explicit(@\seebelow@) optional(optional&&); @@ -143,8 +151,15 @@ constexpr optional& operator=(nullopt_t) noexcept; constexpr optional& operator=(const optional&); constexpr optional& operator=(optional&&) noexcept(@\seebelow@); - template constexpr optional& operator=(U&&); + template> constexpr optional& operator=(U&&); template constexpr optional& operator=(const optional&); +\end{codeblock} +\color{addclr} +\begin{codeblock} + template constexpr optional& operator=(const optional&); +\end{codeblock} +\color{black} +\begin{codeblock} template constexpr optional& operator=(optional&&); template constexpr T& emplace(Args&&...); template constexpr T& emplace(initializer_list, Args&&...); @@ -171,8 +186,8 @@ constexpr T& value() &; // freestanding-deleted constexpr T&& value() &&; // freestanding-deleted constexpr const T&& value() const &&; // freestanding-deleted - template constexpr T value_or(U&&) const &; - template constexpr T value_or(U&&) &&; + template> constexpr T value_or(U&&) const &; + template> constexpr T value_or(U&&) &&; // \ref{optional.monadic}, monadic operations template constexpr auto and_then(F&& f) &; @@ -190,7 +205,7 @@ constexpr void reset() noexcept; private: - T *val; // \expos + T* val; // \expos }; template @@ -202,8 +217,7 @@ Any instance of \tcode{optional} at any given time either contains a value or does not contain a value. When an instance of \tcode{optional} \defnx{contains a value}{contains a value!\idxcode{optional}}, it means that an object of type \tcode{T}, referred to as the optional object's \defnx{contained value}{contained value!\idxcode{optional}}, -is allocated within the storage of the optional object. -Implementations are not permitted to use additional storage, such as dynamic memory, to allocate its contained value. +is nested within\iref{intro.object} the optional object. When an object of type \tcode{optional} is contextually converted to \tcode{bool}, the conversion returns \tcode{true} if the object contains a value; otherwise the conversion returns \tcode{false}. @@ -229,11 +243,691 @@ \rSec3[optional.ctor]{Constructors} +\pnum +The exposition-only variable template \exposid{converts-from-any-cvref} +is used by some constructors for \tcode{optional}. +\begin{codeblock} +template +constexpr bool @\exposid{converts-from-any-cvref}@ = // \expos + disjunction_v, is_convertible, + is_constructible, is_convertible, + is_constructible, is_convertible, + is_constructible, is_convertible>; +\end{codeblock} + +\indexlibraryctor{optional}% +\begin{itemdecl} +constexpr optional() noexcept; +constexpr optional(nullopt_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{*this} does not contain a value. + +\pnum +\remarks +No contained value is initialized. +For every object type \tcode{T} these constructors are constexpr constructors\iref{dcl.constexpr}. +\end{itemdescr} + +\indexlibraryctor{optional}% +\begin{itemdecl} +constexpr optional(const optional& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs} contains a value, direct-non-list-initializes the contained value +with \tcode{*rhs}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +This constructor is defined as deleted unless +\tcode{is_copy_constructible_v} is \tcode{true}. +If \tcode{is_trivially_copy_constructible_v} is \tcode{true}, +this constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{optional}% +\begin{itemdecl} +constexpr optional(optional&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs} contains a value, direct-non-list-initializes the contained value +with \tcode{std::move(*rhs)}. +\tcode{rhs.has_value()} is unchanged. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +The exception specification is equivalent to +\tcode{is_nothrow_move_constructible_v}. +If \tcode{is_trivially_move_constructible_v} is \tcode{true}, +this constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{optional}% +\begin{itemdecl} +template constexpr explicit optional(in_place_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes the contained value with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{*this} contains a value. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +If \tcode{T}'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor. +\end{itemdescr} + +\indexlibraryctor{optional}% +\begin{itemdecl} +template + constexpr explicit optional(in_place_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes the contained value with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{*this} contains a value. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +If \tcode{T}'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor. +\end{itemdescr} + +\indexlibraryctor{optional}% +\begin{itemdecl} +template> constexpr explicit(@\seebelow@) optional(U&& v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_constructible_v} is \tcode{true}, +\item \tcode{is_same_v, in_place_t>} is \tcode{false}, +\item \tcode{is_same_v, optional>} is \tcode{false}, and +\item if \tcode{T} is \cv{} \tcode{bool}, +\tcode{remove_cvref_t} is not a specialization of \tcode{optional}. +\end{itemize} + +\pnum +\effects +Direct-non-list-initializes the contained value with \tcode{std::forward(v)}. + +\pnum +\ensures +\tcode{*this} contains a value. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +If \tcode{T}'s selected constructor is a constexpr constructor, +this constructor is a constexpr constructor. +The expression inside \keyword{explicit} is equivalent to: +\begin{codeblock} +!is_convertible_v +\end{codeblock} +\end{itemdescr} + +\indexlibraryctor{optional}% +\begin{itemdecl} +template constexpr explicit(@\seebelow@) optional(const optional& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_constructible_v} is \tcode{true}, and +\item if \tcode{T} is not \cv{} \tcode{bool}, +\tcode{\exposid{converts-from-any-cvref}>} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs} contains a value, direct-non-list-initializes the contained value with \tcode{*rhs}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +The expression inside \keyword{explicit} is equivalent to: +\begin{codeblock} +!is_convertible_v +\end{codeblock} +\end{itemdescr} + +\begin{addedblock} +\indexlibraryctor{optional}% +\begin{itemdecl} +template constexpr explicit(@\seebelow@) optional(const optional& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_constructible_v} is \tcode{true}, and +\item if \tcode{T} is not \cv{} \tcode{bool}, +\tcode{\exposid{converts-from-any-cvref}>} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs} contains a value, direct-non-list-initializes the contained value with \tcode{*rhs}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +The expression inside \keyword{explicit} is equivalent to: +\begin{codeblock} +!is_convertible_v +\end{codeblock} +\end{itemdescr} +\end{addedblock} + +\indexlibraryctor{optional}% +\begin{itemdecl} +template constexpr explicit(@\seebelow@) optional(optional&& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\begin{addedblock} +\item \tcode{is_reference_v} is \tcode{false}, +\end{addedblock} +\item \tcode{is_constructible_v} is \tcode{true}, and +\item if \tcode{T} is not \cv{} \tcode{bool}, +\tcode{\exposid{converts-from-any-cvref}>} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs} contains a value, +direct-non-list-initializes the contained value with \tcode{std::move(*rhs)}. +\tcode{rhs.has_value()} is unchanged. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +The expression inside \keyword{explicit} is equivalent to: +\begin{codeblock} +!is_convertible_v +\end{codeblock} +\end{itemdescr} \rSec3[optional.dtor]{Destructor} \rSec3[optional.assign]{Assignment} +\indexlibrarymember{operator=}{optional}% +\begin{itemdecl} +constexpr optional& operator=(nullopt_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{*this} contains a value, calls \tcode{val->T::\~T()} to destroy the contained value; otherwise no effect. + +\pnum +\ensures +\tcode{*this} does not contain a value. + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{optional}% +\begin{itemdecl} +constexpr optional& operator=(const optional& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +See \tcode{optional.assign.copy}. +\begin{lib2dtab2}{\tcode{optional::operator=(const optional\&)} effects}{optional.assign.copy} +{\tcode{*this} contains a value} +{\tcode{*this} does not contain a value} + +\rowhdr{\tcode{rhs} contains a value} & +assigns \tcode{*rhs} to the contained value & +direct-non-list-initializes the contained value with \tcode{*rhs} \\ +\rowsep + +\rowhdr{\tcode{rhs} does not contain a value} & +destroys the contained value by calling \tcode{val->T::\~T()} & +no effect \\ +\end{lib2dtab2} + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +If any exception is thrown, the result of the expression \tcode{this->has_value()} remains unchanged. +If an exception is thrown during the call to \tcode{T}'s copy constructor, no effect. +If an exception is thrown during the call to \tcode{T}'s copy assignment, +the state of its contained value is as defined by the exception safety guarantee of \tcode{T}'s copy assignment. +This operator is defined as deleted unless +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_copy_assignable_v} is \tcode{true}. +If \tcode{is_trivially_copy_constructible_v \&\&} +\tcode{is_trivially_copy_assignable_v \&\&} +\tcode{is_trivially_destructible_v} is \tcode{true}, +this assignment operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{optional}% +\begin{itemdecl} +constexpr optional& operator=(optional&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_move_assignable_v} is \tcode{true}. + +\pnum +\effects +See \tcode{optional.assign.move}. +The result of the expression \tcode{rhs.has_value()} remains unchanged. +\begin{lib2dtab2}{\tcode{optional::operator=(optional\&\&)} effects}{optional.assign.move} +{\tcode{*this} contains a value} +{\tcode{*this} does not contain a value} + +\rowhdr{\tcode{rhs} contains a value} & +assigns \tcode{std::move(*rhs)} to the contained value & +direct-non-list-initializes the contained value with \tcode{std::move(*rhs)} \\ +\rowsep + +\rowhdr{\tcode{rhs} does not contain a value} & +destroys the contained value by calling \tcode{val->T::\~T()} & +no effect \\ +\end{lib2dtab2} + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to: +\begin{codeblock} +is_nothrow_move_assignable_v && is_nothrow_move_constructible_v +\end{codeblock} + +\pnum +If any exception is thrown, the result of the expression \tcode{this->has_value()} remains unchanged. +If an exception is thrown during the call to \tcode{T}'s move constructor, +the state of \tcode{*rhs.val} is determined by the exception safety guarantee of \tcode{T}'s move constructor. +If an exception is thrown during the call to \tcode{T}'s move assignment, +the state of \tcode{*val} and \tcode{*rhs.val} is determined by the exception safety guarantee of \tcode{T}'s move assignment. +If \tcode{is_trivially_move_constructible_v \&\&} +\tcode{is_trivially_move_assignable_v \&\&} +\tcode{is_trivially_destructible_v} is \tcode{true}, +this assignment operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{optional}% +\begin{itemdecl} +template> constexpr optional& operator=(U&& v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, optional>} is \tcode{false}, +\item \tcode{conjunction_v, is_same>>} is \tcode{false}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{is_assignable_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +If \tcode{*this} contains a value, assigns \tcode{std::forward(v)} to the contained value; otherwise direct-non-list-initializes the contained value with \tcode{std::forward(v)}. + +\pnum +\ensures +\tcode{*this} contains a value. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +If any exception is thrown, the result of the expression \tcode{this->has_value()} remains unchanged. If an exception is thrown during the call to \tcode{T}'s constructor, the state of \tcode{v} is determined by the exception safety guarantee of \tcode{T}'s constructor. If an exception is thrown during the call to \tcode{T}'s assignment, the state of \tcode{*val} and \tcode{v} is determined by the exception safety guarantee of \tcode{T}'s assignment. +\end{itemdescr} + +\indexlibrarymember{operator=}{optional}% +\begin{itemdecl} +template constexpr optional& operator=(const optional& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\begin{addedblock} +\item \tcode{is_reference_v} is \tcode{false}, +\end{addedblock} +\item \tcode{is_constructible_v} is \tcode{true}, +\item \tcode{is_assignable_v} is \tcode{true}, +\item \tcode{\exposid{converts-from-any-cvref}>} is \tcode{false}, +\item \tcode{is_assignable_v\&>} is \tcode{false}, +\item \tcode{is_assignable_v\&\&>} is \tcode{false}, +\item \tcode{is_assignable_v\&>} is \tcode{false}, and +\item \tcode{is_assignable_v\&\&>} is \tcode{false}. +\end{itemize} + +\pnum +\effects +See \tcode{optional.assign.copy.templ}. +\begin{lib2dtab2}{\tcode{optional::operator=(const optional\&)} effects}{optional.assign.copy.templ} +{\tcode{*this} contains a value} +{\tcode{*this} does not contain a value} + +\rowhdr{\tcode{rhs} contains a value} & +assigns \tcode{*rhs} to the contained value & +direct-non-list-initializes the contained value with \tcode{*rhs} \\ +\rowsep + +\rowhdr{\tcode{rhs} does not contain a value} & +destroys the contained value by calling \tcode{val->T::\~T()} & +no effect \\ +\end{lib2dtab2} + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +If any exception is thrown, +the result of the expression \tcode{this->has_value()} remains unchanged. +If an exception is thrown during the call to \tcode{T}'s constructor, +the state of \tcode{*rhs.val} is determined by +the exception safety guarantee of \tcode{T}'s constructor. +If an exception is thrown during the call to \tcode{T}'s assignment, +the state of \tcode{*val} and \tcode{*rhs.val} is determined by +the exception safety guarantee of \tcode{T}'s assignment. +\end{itemdescr} + +\begin{addedblock} +\indexlibrarymember{operator=}{optional}% +\begin{itemdecl} +template constexpr optional& operator=(const optional& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_constructible_v} is \tcode{true}, +\item \tcode{is_assignable_v} is \tcode{true}, +\item \tcode{\exposid{converts-from-any-cvref}>} is \tcode{false}, +\item \tcode{is_assignable_v\&>} is \tcode{false}, +\item \tcode{is_assignable_v\&\&>} is \tcode{false}, +\item \tcode{is_assignable_v\&>} is \tcode{false}, and +\item \tcode{is_assignable_v\&\&>} is \tcode{false}. +\end{itemize} + +\pnum +\effects +See \tcode{optional.assign.refcopy.templ}. +\begin{lib2dtab2}{\tcode{optional::operator=(const optional\&)} effects}{optional.assign.refcopy.templ} +{\tcode{*this} contains a value} +{\tcode{*this} does not contain a value} + +\rowhdr{\tcode{rhs} contains a value} & +assigns \tcode{*rhs} to the contained value & +direct-non-list-initializes the contained value with \tcode{*rhs} \\ +\rowsep + +\rowhdr{\tcode{rhs} does not contain a value} & +destroys the contained value by calling \tcode{val->T::\~T()} & +no effect \\ +\end{lib2dtab2} + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +If any exception is thrown, +the result of the expression \tcode{this->has_value()} remains unchanged. +If an exception is thrown during the call to \tcode{T}'s constructor, +the state of \tcode{*rhs.val} is determined by +the exception safety guarantee of \tcode{T}'s constructor. +If an exception is thrown during the call to \tcode{T}'s assignment, +the state of \tcode{*val} and \tcode{*rhs.val} is determined by +the exception safety guarantee of \tcode{T}'s assignment. +\end{itemdescr} +\end{addedblock} + +\indexlibrarymember{operator=}{optional}% +\begin{itemdecl} +template constexpr optional& operator=(optional&& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\begin{addedblock} +\item \tcode{is_reference_v} is \tcode{false}, +\end{addedblock} +\item \tcode{is_constructible_v} is \tcode{true}, +\item \tcode{is_assignable_v} is \tcode{true}, +\item \tcode{\exposid{converts-from-any-cvref}>} is \tcode{false}, +\item \tcode{is_assignable_v\&>} is \tcode{false}, +\item \tcode{is_assignable_v\&\&>} is \tcode{false}, +\item \tcode{is_assignable_v\&>} is \tcode{false}, and +\item \tcode{is_assignable_v\&\&>} is \tcode{false}. +\end{itemize} + +\pnum +\effects +See \tcode{optional.assign.move.templ}. +The result of the expression \tcode{rhs.has_value()} remains unchanged. +\begin{lib2dtab2}{\tcode{optional::operator=(optional\&\&)} effects}{optional.assign.move.templ} +{\tcode{*this} contains a value} +{\tcode{*this} does not contain a value} + +\rowhdr{\tcode{rhs} contains a value} & +assigns \tcode{std::move(*rhs)} to the contained value & +direct-non-list-initializes the contained value with \tcode{std::move(*rhs)} \\ +\rowsep + +\rowhdr{\tcode{rhs} does not contain a value} & +destroys the contained value by calling \tcode{val->T::\~T()} & +no effect \\ +\end{lib2dtab2} + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +If any exception is thrown, +the result of the expression \tcode{this->has_value()} remains unchanged. +If an exception is thrown during the call to \tcode{T}'s constructor, +the state of \tcode{*rhs.val} is determined by +the exception safety guarantee of \tcode{T}'s constructor. +If an exception is thrown during the call to \tcode{T}'s assignment, +the state of \tcode{*val} and \tcode{*rhs.val} is determined by +the exception safety guarantee of \tcode{T}'s assignment. +\end{itemdescr} + +\indexlibrarymember{emplace}{optional}% +\begin{itemdecl} +template constexpr T& emplace(Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Calls \tcode{*this = nullopt}. Then direct-non-list-initializes the contained value +with \tcode{std::forward\brk{}(args)...}. + +\pnum +\ensures +\tcode{*this} contains a value. + +\pnum +\returns +A reference to the new contained value. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +If an exception is thrown during the call to \tcode{T}'s constructor, \tcode{*this} does not contain a value, and the previous \tcode{*val} (if any) has been destroyed. +\end{itemdescr} + +\indexlibrarymember{emplace}{optional}% +\begin{itemdecl} +template constexpr T& emplace(initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Calls \tcode{*this = nullopt}. Then direct-non-list-initializes the contained value with +\tcode{il, std::\brk{}forward(args)...}. + +\pnum +\ensures +\tcode{*this} contains a value. + +\pnum +\returns +A reference to the new contained value. + +\pnum +\throws +Any exception thrown by the selected constructor of \tcode{T}. + +\pnum +\remarks +If an exception is thrown during the call to \tcode{T}'s constructor, \tcode{*this} does not contain a value, and the previous \tcode{*val} (if any) has been destroyed. +\end{itemdescr} + \rSec3[optional.swap]{Swap} diff --git a/papers/P2988/optional_ref_wording.tex b/papers/P2988/optional_ref_wording.tex index 0ec137f0..6a502559 100644 --- a/papers/P2988/optional_ref_wording.tex +++ b/papers/P2988/optional_ref_wording.tex @@ -18,7 +18,7 @@ \begin{flushright} \begin{tabular}{ll} - Document \#: & P2988R8 \\ + Document \#: & P2988R9 \\ Date: & \today \\ Project: & Programming Language C++ \\ Audience: & LEWG @@ -36,6 +36,10 @@ \chapter*{Changes Since Last Version} \begin{itemize} +\item \textbf{Changes since R8} + \begin{itemize} + \item Fix move/assign optional allowing stealing of referenced U + \end{itemize} \item \textbf{Changes since R7} \begin{itemize} \item Wording mandates/constraint fixes @@ -384,6 +388,9 @@ \section{Assignment of optional} This observation allows us to provide only copy-assignment for \tcode{optional}, instead of a set of converting assignments, that would need to replicate the signatures of constructors and their constraints. Assignment from any other value is handled by first implicitly constructing \tcode{optional} and then using copy-assignment. Move-assignment is the same as copy-assignment, since only pointer copy is involved. +\section{Copy and Assignment of optional\&\& to optional} +Care must be take to prevent the assignment of a movable optional to disallow the copy or assignment of the underlying referred to value to be stolen. The \tcode{optional::optional const\&} assignment or copy constructor should be used instead, which also needs to check slightly different constraints for \tcode{converts-from-any-cvref} and for testing \tcode{is_assignable}. We thank Jan Kokemüller for uncovering this bug. The bug seemns to be present in many optional implementations that support references. + \chapter{Proposal} @@ -406,6 +413,10 @@ \chapter{Impact on the standard} The proposed changes are relative to the current working draft \cite{N4910}. +\chapter{Acknowledgements} +Many thanks to all of the reviewers and authors of beman/optional26, \cite{The_Beman_Project_beman_optional26}, in particular A. Jiang, Darius Neațu, David Sankel, Eddie Nolan, Jan Kokemüller, Jeff Garland, and River. Tomasz Kamiński provided extensive support for the library wording of optional. + + \chapter*{Document history} \begin{itemize}