From a91769e813f4855038f0984a4f3a5123f56a81fd Mon Sep 17 00:00:00 2001 From: Dawn Perchik Date: Tue, 27 Jun 2023 01:01:57 -0700 Subject: [PATCH 1/4] P0792R14 function_ref: a type-erased callable reference Editorial notes: - [func.wrap.ref.class] Fix reference for "trivially copyable type". - [func.wrap.ref.class] Change "this subclause" to "subclause [func.wrap.ref]" to account for the external use of "call-args". --- source/support.tex | 1 + source/utilities.tex | 367 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 366 insertions(+), 2 deletions(-) diff --git a/source/support.tex b/source/support.tex index 5675f7dbff..f446e54830 100644 --- a/source/support.tex +++ b/source/support.tex @@ -648,6 +648,7 @@ #define @\defnlibxname{cpp_lib_freestanding_ratio}@ 202306L // freestanding, also in \libheader{ratio} #define @\defnlibxname{cpp_lib_freestanding_tuple}@ 202306L // freestanding, also in \libheader{tuple} #define @\defnlibxname{cpp_lib_freestanding_utility}@ 202306L // freestanding, also in \libheader{utility} +#define @\defnlibxname{cpp_lib_function_ref}@ 202306L // also in \libheader{functional} #define @\defnlibxname{cpp_lib_gcd_lcm}@ 201606L // also in \libheader{numeric} #define @\defnlibxname{cpp_lib_generator}@ 202207L // also in \libheader{generator} #define @\defnlibxname{cpp_lib_generic_associative_lookup}@ 201304L // also in \libheader{map}, \libheader{set} diff --git a/source/utilities.tex b/source/utilities.tex index ab5db626f6..c71474ce66 100644 --- a/source/utilities.tex +++ b/source/utilities.tex @@ -214,6 +214,15 @@ explicit in_place_index_t() = default; }; template constexpr in_place_index_t in_place_index{}; + + // \tcode{nontype} argument tag% +\indexlibraryglobal{nontype_t}% +\indexlibraryglobal{nontype} + template + struct nontype_t { + explicit nontype_t() = default; + }; + template constexpr nontype_t nontype{}; } \end{codeblock} @@ -10576,9 +10585,14 @@ bool operator==(const function&, nullptr_t) noexcept; // \ref{func.wrap.move}, move only wrapper - template class move_only_function; // \notdef + template class move_only_function; // \notdef + template + class move_only_function; // \seebelow + + // \ref{func.wrap.ref}, non-owning wrapper + template class function_ref; // freestanding, \notdef template - class move_only_function; // \seebelow + class function_ref; // freestanding, \seebelow // \ref{func.search}, searchers template> @@ -13438,6 +13452,355 @@ \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} + +\pnum +The header provides partial specializations of \tcode{function_ref} +for each combination of the possible replacements of +the placeholders \cv{} and \placeholder{noex} where: + +\begin{itemize} +\item \cv{} is either const or empty, and +\item \placeholder{noex} is either \tcode{true} or \tcode{false}. +\end{itemize} + +\rSec4[func.wrap.ref.class]{Class template \tcode{function_ref}} + +\indexlibraryglobal{function_ref}% +\begin{codeblock} +namespace std { + template class function_ref; // \notdef + + template + class function_ref { + public: + // \ref{func.wrap.ref.ctor}, constructors and assignment operators + template function_ref(F*) noexcept; + template constexpr function_ref(F&&) noexcept; + template constexpr function_ref(nontype_t) noexcept; + template constexpr function_ref(nontype_t, U&&) noexcept; + template constexpr function_ref(nontype_t, @\cv{}@ T*) noexcept; + + constexpr function_ref(const function_ref&) noexcept = default; + constexpr function_ref& operator=(const function_ref&) noexcept = default; + template function_ref& operator=(T) = delete; + + // \ref{func.wrap.ref.inv}, invocation + R operator()(ArgTypes...) const noexcept(@\placeholder{noex}@); + + private: + template + static constexpr bool @\exposid{is-invocable-using}@ = @\seebelow@; // \expos + }; + + // \ref{func.wrap.ref.deduct}, deduction guides + template + function_ref(F*) -> function_ref; + template + function_ref(nontype_t) -> function_ref<@\seebelow@>; + template + function_ref(nontype_t, auto) -> function_ref<@\seebelow@>; +} +\end{codeblock} + +\pnum +An object of class +\tcode{function_ref} +stores +%FIXME: What is "thunk-ptr"? where did it come from? +a pointer to function \exposid{thunk-ptr} and +%FIXME: What is "bound-entity"? where did it come from? +an object \exposid{bound-entity}. +\exposid{bound-entity} has +an unspecified trivially copyable type \tcode{BoundEntityType}, that +models \libconcept{copyable} and +is capable of storing a pointer to object value or a pointer to function value. +The type of \exposid{thunk-ptr} is +\tcode{R(*)(BoundEntityType, Args\&\&...) noexcept(\placeholder{noex})}. + +\pnum +Each specialization of \tcode{function_ref} is +a trivially copyable type\iref{term.trivially.copyable.type} +that models \libconcept{copyable}. + +\pnum +Within subclause \ref{func.wrap.ref}, +\tcode{\placeholder{call-args}} is an argument pack with elements such that +\tcode{decltype((\placeholder{call-args}\linebreak{}))...} denote +\tcode{Args\&\&...} respectively. + +\rSec4[func.wrap.ref.ctor]{Constructors and assignment operators} + +\indextext{function_ref::is-invocable-using@\tcode{function_ref::\exposid{is-invocable-using}}}% +\begin{itemdecl} +template + static constexpr bool @\exposid{is-invocable-using}@ = @\seebelow@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +If \placeholder{noex} is \tcode{true}, +\tcode{\exposid{is-invocable-using}} is equal to: +\begin{codeblock} +is_nothrow_invocable_r_v +\end{codeblock} +Otherwise, \tcode{\exposid{is-invocable-using}} is equal to: +\begin{codeblock} +is_invocable_r_v +\end{codeblock} +\end{itemdescr} + +\indexlibraryctor{function_ref}% +\begin{itemdecl} +template function_ref(F* f) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_function_v} is \tcode{true}, and +\item \tcode{\exposid{is-invocable-using}} is \tcode{true}. +\end{itemize} + +\pnum +\expects +\tcode{f} is not a null pointer. + +\pnum +\effects +Initializes +\exposid{bound-entity} with \tcode{f}, and +\exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} +such that +\tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} +is expression-equivalent to +\tcode{invoke_r(f, \placeholder{call-args}...)}. +\end{itemdescr} + +\indexlibraryctor{function_ref}% +\begin{itemdecl} +template constexpr function_ref(F&& f) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{T} be \tcode{remove_reference_t}. + +\pnum +\constraints +\begin{itemize} +\item \tcode{remove_cvref_t} is not the same type as \tcode{function_ref}, +\item \tcode{is_member_pointer_v} is \tcode{false}, and +\item \tcode{\exposid{is-invocable-using}<\cv{} T\&>} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Initializes +\exposid{bound-entity} with \tcode{addressof(f)}, and +\exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} +such that +\tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} +is expression-equivalent to +\tcode{invoke_r(static_cast<\cv{} T\&>(f), \placeholder{call-args}...)}. +\end{itemdescr} + +\indexlibraryctor{function_ref}% +\begin{itemdecl} +template constexpr function_ref(nontype_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{F} be \tcode{decltype(f)}. + +\pnum +\constraints +\tcode{\exposid{is-invocable-using}} is \tcode{true}. + +\pnum +\mandates +If \tcode{is_pointer_v || is_member_pointer_v} is \tcode{true}, +then \tcode{f != nullptr} is \tcode{true}. + +\pnum +\effects +Initializes +\exposid{bound-entity} with a pointer to an unspecified object or +null pointer value, and +\exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} +such that +\tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} +is expression-equivalent to +\tcode{invoke_r(f, \placeholder{call-args}...)}. +\end{itemdescr} + +\indexlibraryctor{function_ref}% +\begin{itemdecl} +template + constexpr function_ref(nontype_t, U&& obj) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{T} be \tcode{remove_reference_t} and +\tcode{F} be \tcode{decltype(f)}. + +\pnum +\constraints +\begin{itemize} +\item \tcode{is_rvalue_reference_v} is \tcode{false}, and +\item \tcode{\exposid{is-invocable-using}} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +If \tcode{is_pointer_v || is_member_pointer_v} is \tcode{true}, +then \tcode{f != nullptr} is \tcode{true}. + +\pnum +\effects +Initializes +\exposid{bound-entity} with \tcode{addressof(obj)}, and +\exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} +such that +\tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} +is expression-equivalent to +\tcode{invoke_r(f, static_cast<\cv{} T\&>(obj), \placeholder{call-args}...)}. +\end{itemdescr} + +\indexlibraryctor{function_ref}% +\begin{itemdecl} +template + constexpr function_ref(nontype_t, @\cv{}@ T* obj) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{F} be \tcode{decltype(f)}. + +\pnum +\constraints +\tcode{\exposid{is-invocable-using}} is \tcode{true}. + +\pnum +\mandates +If \tcode{is_pointer_v || is_member_pointer_v} is \tcode{true}, +then \tcode{f != nullptr} is \tcode{true}. + +\pnum +\expects +If \tcode{is_member_pointer_v} is \tcode{true}, +\tcode{obj} is not a null pointer. + +\pnum +\effects +Initializes +\exposid{bound-entity} with \tcode{obj}, and +\exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} +such that +\tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} +is expression-equivalent to +\tcode{invoke_r(f, obj, \placeholder{call-args}...)}. +\end{itemdescr} + +\indexlibrarymember{operator=}{function_ref}% +\begin{itemdecl} +template function_ref& operator=(T) = delete; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{T} is not the same type as \tcode{function_ref}, +\item \tcode{is_pointer_v} is \tcode{false}, and +\item \tcode{T} is not a specialization of \tcode{nontype_t}. +\end{itemize} +\end{itemdescr} + +\rSec4[func.wrap.ref.inv]{Invocation} + +\indexlibrarymember{operator()}{function_ref}% +\begin{itemdecl} +R operator()(ArgTypes... args) const noexcept(@\placeholder{noex}@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\tcode{return \exposid{thunk-ptr}(\exposid{bound-entity}, std::forward(args)...);} +\end{itemdescr} + +\rSec4[func.wrap.ref.deduct]{Deduction guides} + +\begin{itemdecl} +template + function_ref(F*) -> function_ref; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_function_v} is \tcode{true}. +\end{itemdescr} + +\begin{itemdecl} +template + function_ref(nontype_t) -> function_ref<@\seebelow@>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{F} be \tcode{remove_pointer_t}. + +\pnum +\constraints +\tcode{is_function_v} is \tcode{true}. + +\pnum +\remarks +The deduced type is \tcode{function_ref}. +\end{itemdescr} + +\begin{itemdecl} +template + function_ref(nontype_t, T&&) -> function_ref<@\seebelow@>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{F} be \tcode{decltype(f)}. + +\pnum +\constraints +%FIXME: R and E should be defined outside of these constraints. +%FIXME: Define R and E via "let" in paragraph above, then use them here and below. +\begin{itemize} +\item +\tcode{F} is of the form +\tcode{R(G::*)(A...) \cv{} \opt{\&} noexcept(E)} for a type \tcode{G}, or +\item +\tcode{F} is of the form +\tcode{M G::*} for a type \tcode{G} and an object type \tcode{M}, +in which case +let \tcode{R} be \tcode{invoke_result_t}, +\tcode{A...} be an empty pack, and +\tcode{E} be \tcode{false}, or +\item +\tcode{F} is of the form +\tcode{R(*)(G, A...) noexcept(E)} for a type \tcode{G}. +\end{itemize} + +\pnum +\remarks +The deduced type is \tcode{function_ref}. +\end{itemdescr} \indextext{function object!wrapper|)} \rSec2[func.search]{Searchers} From 08defadee44ce23a44ab8a7ec437f08a894b5a5f Mon Sep 17 00:00:00 2001 From: Dawn Perchik Date: Tue, 27 Jun 2023 20:06:46 -0700 Subject: [PATCH 2/4] [func.wrap.ref] Add references to [defns.expression.equivalent] on "expression-equivalent" --- source/utilities.tex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/utilities.tex b/source/utilities.tex index c71474ce66..ef64169e91 100644 --- a/source/utilities.tex +++ b/source/utilities.tex @@ -13577,7 +13577,7 @@ \exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} such that \tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} -is expression-equivalent to +is expression-equivalent\iref{defns.expression.equivalent} to \tcode{invoke_r(f, \placeholder{call-args}...)}. \end{itemdescr} @@ -13605,7 +13605,7 @@ \exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} such that \tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} -is expression-equivalent to +is expression-equivalent\iref{defns.expression.equivalent} to \tcode{invoke_r(static_cast<\cv{} T\&>(f), \placeholder{call-args}...)}. \end{itemdescr} @@ -13635,7 +13635,7 @@ \exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} such that \tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} -is expression-equivalent to +is expression-equivalent\iref{defns.expression.equivalent} to \tcode{invoke_r(f, \placeholder{call-args}...)}. \end{itemdescr} @@ -13669,7 +13669,7 @@ \exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} such that \tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} -is expression-equivalent to +is expression-equivalent\iref{defns.expression.equivalent} to \tcode{invoke_r(f, static_cast<\cv{} T\&>(obj), \placeholder{call-args}...)}. \end{itemdescr} @@ -13704,7 +13704,7 @@ \exposid{thunk-ptr} with the address of a function \tcode{\placeholder{thunk}} such that \tcode{\placeholder{thunk}(\exposid{bound-entity}, \placeholder{call-args}...)} -is expression-equivalent to +is expression-equivalent\iref{defns.expression.equivalent} to \tcode{invoke_r(f, obj, \placeholder{call-args}...)}. \end{itemdescr} From 33d767583359e22fed2dcfaa3b27fe90c7dd8329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Fri, 21 Jul 2023 17:38:55 +0100 Subject: [PATCH 3/4] [func.wrap.ref.class] Add expos-only members explicitly --- source/utilities.tex | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/utilities.tex b/source/utilities.tex index ef64169e91..06c9641bc1 100644 --- a/source/utilities.tex +++ b/source/utilities.tex @@ -13493,7 +13493,10 @@ private: template - static constexpr bool @\exposid{is-invocable-using}@ = @\seebelow@; // \expos + static constexpr bool @\exposidnc{is-invocable-using}@ = @\seebelownc@; // \expos + + R (*@\exposidnc{thunk-ptr}@)(@\exposidnc{BoundEntityType}@, Args&&...) noexcept(@\placeholdernc{noex}@); // \expos + @\exposidnc{BoundEntityType}@ @\exposidnc{bound-entity}@; // \expos }; // \ref{func.wrap.ref.deduct}, deduction guides @@ -13509,17 +13512,14 @@ \pnum An object of class \tcode{function_ref} -stores -%FIXME: What is "thunk-ptr"? where did it come from? -a pointer to function \exposid{thunk-ptr} and -%FIXME: What is "bound-entity"? where did it come from? +stores a pointer to function \exposid{thunk-ptr} and an object \exposid{bound-entity}. \exposid{bound-entity} has -an unspecified trivially copyable type \tcode{BoundEntityType}, that +an unspecified trivially copyable type \exposid{BoundEntityType}, that models \libconcept{copyable} and is capable of storing a pointer to object value or a pointer to function value. The type of \exposid{thunk-ptr} is -\tcode{R(*)(BoundEntityType, Args\&\&...) noexcept(\placeholder{noex})}. +\tcode{R(*)(\exposidnc{BoundEntityType}, Args\&\&...) noexcept(\placeholder{noex})}. \pnum Each specialization of \tcode{function_ref} is From 4066e89db967b1acbc47777c210259dfd60cca78 Mon Sep 17 00:00:00 2001 From: Dawn Perchik Date: Tue, 27 Jun 2023 20:16:15 -0700 Subject: [PATCH 4/4] [func.wrap.ref.class] Remove redundant declaration for function_ref as per #6273. --- source/utilities.tex | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/utilities.tex b/source/utilities.tex index 06c9641bc1..fdc9737574 100644 --- a/source/utilities.tex +++ b/source/utilities.tex @@ -13472,8 +13472,6 @@ \indexlibraryglobal{function_ref}% \begin{codeblock} namespace std { - template class function_ref; // \notdef - template class function_ref { public: