diff --git a/source/support.tex b/source/support.tex index 9e4df0075a..51c24ba8ec 100644 --- a/source/support.tex +++ b/source/support.tex @@ -616,6 +616,7 @@ #define @\defnlibxname{cpp_lib_containers_ranges}@ 202202L // also in \libheader{vector}, \libheader{list}, \libheader{forward_list}, \libheader{map}, \libheader{set}, \libheader{unordered_map}, \libheader{unordered_set}, // \libheader{deque}, \libheader{queue}, \libheader{stack}, \libheader{string} +#define @\defnlibxname{cpp_lib_copyable_function}@ 202306L // also in \libheader{functional} #define @\defnlibxname{cpp_lib_coroutine}@ 201902L // also in \libheader{coroutine} #define @\defnlibxname{cpp_lib_destroying_delete}@ 201806L // freestanding, also in \libheader{new} #define @\defnlibxname{cpp_lib_enable_shared_from_this}@ 201603L // also in \libheader{memory} diff --git a/source/utilities.tex b/source/utilities.tex index 9bc7a9a8a4..6c81969930 100644 --- a/source/utilities.tex +++ b/source/utilities.tex @@ -10649,6 +10649,11 @@ template class move_only_function; // \seebelow + // \ref{func.wrap.copy}, copyable wrapper + template class copyable_function; // \notdef + template + class copyable_function; // \seebelow + // \ref{func.wrap.ref}, non-owning wrapper template class function_ref; // freestanding, \notdef template @@ -12639,6 +12644,30 @@ Subclause \ref{func.wrap} describes polymorphic wrapper classes that encapsulate arbitrary callable objects. +\pnum +Let \tcode{t} be an object of a type that is a specialization of +\tcode{function}, \tcode{copyable_function}, or \tcode{move_only_function}, +such that the target object \tcode{x} of \tcode{t} has a type that +is a specialization of +\tcode{function}, \tcode{copyable_function}, or \tcode{move_only_function}. +Each argument of the +invocation of \tcode{x} evaluated as part of the invocation of \tcode{t} +may alias an argument in the same position in the invocation of \tcode{t} that +has the same type, even if the corresponding parameter is not of reference type. +\begin{example} +\begin{codeblock} +move_only_function + f{copyable_function{[](T) {}}}; +T t; +f(t); // it is unspecified how many copies of \tcode{T} are made +\end{codeblock} +\end{example} + +\pnum +\recommended +Implementations should avoid double wrapping when +constructing polymorphic wrappers from one another. + \rSec3[func.wrap.badcall]{Class \tcode{bad_function_call}}% \indexlibraryglobal{bad_function_call}% @@ -13513,6 +13542,446 @@ \tcode{true} if \tcode{f} has no target object, otherwise \tcode{false}. \end{itemdescr} +\rSec3[func.wrap.copy]{Copyable wrapper} + +\rSec4[func.wrap.copy.general]{General} + +\pnum +The header provides partial specializations of \tcode{copyable_function} +for each combination of the possible replacements +of the placeholders \cv{}, \placeholder{ref}, and \placeholder{noex} where +\begin{itemize} +\item +\cv{} is either const or empty, +\item +\placeholder{ref} is either \tcode{\&}, \tcode{\&\&}, or empty, and +\item +\placeholder{noex} is either \tcode{true} or \tcode{false}. +\end{itemize} + +\pnum +For each of the possible combinations of the placeholders mentioned above, +there is a placeholder \placeholder{inv-quals} defined as follows: +\begin{itemize} +\item +If \placeholder{ref} is empty, let \placeholder{inv-quals} be \cv{}\tcode{\&}, +\item +otherwise, let \placeholder{inv-quals} be \cv{} \placeholder{ref}. +\end{itemize} + +\rSec4[func.wrap.copy.class]{Class template \tcode{copyable_function}} + +\indexlibraryglobal{copyable_function}% +\begin{codeblock} +namespace std { + template + class copyable_function { + public: + using result_type = R; + + // \ref{func.wrap.copy.ctor}, constructors, assignments, and destructors + copyable_function() noexcept; + copyable_function(nullptr_t) noexcept; + copyable_function(const copyable_function&); + copyable_function(copyable_function&&) noexcept; + template copyable_function(F&&); + template + explicit copyable_function(in_place_type_t, Args&&...); + template + explicit copyable_function(in_place_type_t, initializer_list, Args&&...); + + copyable_function& operator=(const copyable_function&); + copyable_function& operator=(copyable_function&&); + copyable_function& operator=(nullptr_t) noexcept; + template copyable_function& operator=(F&&); + + ~copyable_function(); + + // \ref{func.wrap.copy.inv}, invocation + explicit operator bool() const noexcept; + R operator()(ArgTypes...) @\cv{}@ @\placeholder{ref}@ noexcept(@\placeholder{noex}@); + + // \ref{func.wrap.copy.util}, utility + void swap(copyable_function&) noexcept; + friend void swap(copyable_function&, copyable_function&) noexcept; + friend bool operator==(const copyable_function&, nullptr_t) noexcept; + + private: + template + static constexpr bool @\exposid{is-callable-from}@ = @\seebelow@; // \expos + }; +} +\end{codeblock} + +\pnum +The \tcode{copyable_function} class template provides polymorphic wrappers +that generalize the notion of a callable object\iref{func.def}. +These wrappers can store, copy, move, and call arbitrary callable objects, +given a call signature. + +\pnum +\recommended +Implementations should avoid the use of dynamically allocated memory +for a small contained value. +\begin{note} +Such small-object optimization can only be applied to a type \tcode{T} +for which \tcode{is_nothrow_move_constructible_v} is \tcode{true}. +\end{note} + +\rSec4[func.wrap.copy.ctor]{Constructors, assignments, and destructors} + +\indextext{copyable_function::is-callable-from@\tcode{copyable_function::\exposid{is-callable-from}}}% +\begin{itemdecl} +template + static constexpr bool @\exposid{is-callable-from}@ = @\seebelow@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +If \placeholder{noex} is \tcode{true}, +\tcode{\exposid{is-callable-from}} is equal to: +\begin{codeblock} +is_nothrow_invocable_r_v && +is_nothrow_invocable_r_v +\end{codeblock} +Otherwise, \tcode{\exposid{is-callable-from}} is equal to: +\begin{codeblock} +is_invocable_r_v && +is_invocable_r_v +\end{codeblock} +\end{itemdescr} + +\indexlibraryctor{copyable_function}% +\begin{itemdecl} +copyable_function() noexcept; +copyable_function(nullptr_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{*this} has no target object. +\end{itemdescr} + +\indexlibraryctor{copyable_function}% +\begin{itemdecl} +copyable_function(const copyable_function& f); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{*this} has no target object if \tcode{f} had no target object. +Otherwise, the target object of \tcode{*this} +is a copy of the target object of \tcode{f}. + +\pnum +\throws +Any exception thrown by the initialization of the target object. +May throw \tcode{bad_alloc}. +\end{itemdescr} + +\indexlibraryctor{copyable_function}% +\begin{itemdecl} +copyable_function(copyable_function&& f) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +The target object of \tcode{*this} is +the target object \tcode{f} had before construction, and +\tcode{f} is in a valid state with an unspecified value. +\end{itemdescr} + +\indexlibraryctor{copyable_function}% +\begin{itemdecl} +template copyable_function(F&& f); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{VT} be \tcode{decay_t}. + +\pnum +\constraints +\begin{itemize} +\item +\tcode{remove_cvref_t} is not the same type as \tcode{copyable_function}, and +\item +\tcode{remove_cvref_t} is not a specialization of \tcode{in_place_type_t}, and +\item +\tcode{\exposid{is-callable-from}} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\begin{itemize} +\item +\tcode{is_constructible_v} is \tcode{true}, and +\item +\tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\expects +\tcode{VT} meets the \oldconcept{Destructible} and +\oldconcept{CopyConstructible} requirements. + +\pnum +\ensures +\tcode{*this} has no target object if any of the following hold: +\begin{itemize} +\item +\tcode{f} is a null function pointer value, or +\item +\tcode{f} is a null member pointer value, or +\item +\tcode{remove_cvref_t} is a specialization of +the \tcode{copyable_function} class template, +and \tcode{f} has no target object. +\end{itemize} +Otherwise, \tcode{*this} has a target object of type \tcode{VT} +direct-non-list-initialized with \tcode{std::forward(f)}. + +\pnum +\throws +Any exception thrown by the initialization of the target object. +May throw \tcode{bad_alloc} unless \tcode{VT} is +a function pointer or a specialization of \tcode{reference_wrapper}. +\end{itemdescr} + +\indexlibraryctor{copyable_function}% +\begin{itemdecl} +template + explicit copyable_function(in_place_type_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{VT} be \tcode{decay_t}. + +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_constructible_v} is \tcode{true}, and +\item +\tcode{\exposid{is-callable-from}} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\begin{itemize} +\item +\tcode{VT} is the same type as \tcode{T}, and +\item +\tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\expects +\tcode{VT} meets the \oldconcept{Destructible} and +\oldconcept{CopyConstructible} requirements. + +\pnum +\ensures +\tcode{*this} has a target object of type \tcode{VT} +direct-non-list-initialized with \tcode{std::forward\brk{}(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of the target object. +May throw \tcode{bad_alloc} unless \tcode{VT} is +a pointer or a specialization of \tcode{reference_wrapper}. +\end{itemdescr} + +\indexlibraryctor{copyable_function}% +\begin{itemdecl} +template + explicit copyable_function(in_place_type_t, initializer_list ilist, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{VT} be \tcode{decay_t}. + +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_constructible_v\&, Args...>} is +\tcode{true}, and +\item +\tcode{\exposid{is-callable-from}} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\begin{itemize} +\item +\tcode{VT} is the same type as \tcode{T}, and +\item +\tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\expects +\tcode{VT} meets the \oldconcept{Destructible} and +\oldconcept{CopyConstructible} requirements. + +\pnum +\ensures +\tcode{*this} has a target object of type \tcode{VT} +direct-non-list-initialized with +\tcode{ilist, std::for\-ward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of the target object. +May throw \tcode{bad_alloc} unless \tcode{VT} is +a pointer or a specialization of \tcode{reference_wrapper}. +\end{itemdescr} + +\indexlibrarymember{operator=}{copyable_function}% +\begin{itemdecl} +copyable_function& operator=(const copyable_function& f); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{copyable_function(f).swap(*this);} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{copyable_function}% +\begin{itemdecl} +copyable_function& operator=(copyable_function&& f); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{copyable_function(std::move(f)).swap(*this);} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{copyable_function}% +\begin{itemdecl} +copyable_function& operator=(nullptr_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Destroys the target object of \tcode{*this}, if any. + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{copyable_function}% +\begin{itemdecl} +template copyable_function& operator=(F&& f); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{copyable_function(std::forward(f)).swap(*this);} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarydtor{copyable_function}% +\begin{itemdecl} +~copyable_function(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Destroys the target object of \tcode{*this}, if any. +\end{itemdescr} + +\rSec4[func.wrap.copy.inv]{Invocation} + +\indexlibrarymember{operator bool}{copyable_function}% +\begin{itemdecl} +explicit operator bool() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{true} if \tcode{*this} has a target object, otherwise \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{operator()}{copyable_function}% +\begin{itemdecl} +R operator()(ArgTypes... args) @\cv{}@ @\placeholder{ref}@ noexcept(@\placeholder{noex}@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{*this} has a target object. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +return @\placeholder{INVOKE}@(static_cast(f), std::forward(args)...); +\end{codeblock} +where \tcode{f} is an lvalue designating the target object of \tcode{*this} and +\tcode{F} is the type of \tcode{f}. +\end{itemdescr} + +\rSec4[func.wrap.copy.util]{Utility} + +\indexlibrarymember{swap}{copyable_function}% +\begin{itemdecl} +void swap(copyable_function& other) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Exchanges the target objects of \tcode{*this} and \tcode{other}. +\end{itemdescr} + +\indexlibrarymember{swap}{copyable_function}% +\begin{itemdecl} +friend void swap(copyable_function& f1, copyable_function& f2) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{f1.swap(f2)}. +\end{itemdescr} + +\indexlibrarymember{operator==}{copyable_function}% +\begin{itemdecl} +friend bool operator==(const copyable_function& f, nullptr_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{true} if \tcode{f} has no target object, otherwise \tcode{false}. +\end{itemdescr} + \rSec3[func.wrap.ref]{Non-owning wrapper} \rSec4[func.wrap.ref.general]{General}