From c81d8e04b9cab7c9e0fb322194f110729913044c Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sat, 30 Nov 2024 23:34:53 +0100 Subject: [PATCH] P3019R11 indirect and polymorphic: Vocabulary Types for Composite Class Design - Rename lhs and rhs parameters in comparison functions. - Rephase notes with bad grammar. --- source/memory.tex | 1511 ++++++++++++++++++++++++++++++++++++++++++++ source/support.tex | 2 + 2 files changed, 1513 insertions(+) diff --git a/source/memory.tex b/source/memory.tex index 1260365953..8d110e384c 100644 --- a/source/memory.tex +++ b/source/memory.tex @@ -583,6 +583,22 @@ // \ref{inout.ptr}, function template \tcode{inout_ptr} template auto inout_ptr(Smart& s, Args&&... args); // freestanding + + // \ref{indirect}, class template \tcode{indirect} + template> + class indirect; + + // \ref{indirect.hash}, hash support + template struct hash>; + + // \ref{polymorphic}, class template \tcode{polymorphic} + template> + class polymorphic; + + namespace pmr { + template using indirect = indirect>; + template using polymorphic = polymorphic>; + } } \end{codeblock} @@ -7149,3 +7165,1498 @@ a.outer_allocator() == b.outer_allocator() && a.inner_allocator() == b.inner_allocator() \end{codeblock} \end{itemdescr} + +\rSec1[indirect]{Class template \tcode{indirect}} + +\rSec2[indirect.general]{General} + +\pnum +An indirect object manages the lifetime of an owned object. +An indirect object is \defnx{valueless}{valueless!indirect} +if it has no owned object. +An indirect object may only become valueless after it has been moved from. + +\pnum +In every specialization \tcode{indirect}, +if the type \tcode{allocator_traits::value_type} +is not the same type as \tcode{T}, +the program is ill-formed. +Every object of type \tcode{indirect} +uses an object of type \tcode{Allocator} +to allocate and free storage for the owned object as needed. + +\pnum +Constructing an owned object with \tcode{args...} using the allocator \tcode{a} +means evaluating \tcode{allocator_traits::construct(a, p, args...)} +where \tcode{args} is an expression pack, +\tcode{a} is an allocator, and +\tcode{p} is a pointer obtained by +calling \tcode{allocator_traits::allocate}. + +\pnum +The member \exposid{alloc} is used for any memory allocation and +element construction performed by member functions +during the lifetime of each indirect object. +The allocator \exposid{alloc} may only be replaced +via assignment or \tcode{swap}. +Allocator replacement is performed by +copy assignment, move assignment, or swapping of the allocator +only if +\begin{itemize} +\item +\tcode{allocator_traits::propagate_on_container_copy_assignment::value}, +\item +\tcode{allocator_traits::propagate_on_container_move_assignment::value}, or +\item +\tcode{allocator_traits::propagate_on_container_swap::value} +\end{itemize} +is \tcode{true} +within the implementation of +the corresponding indirect operation\iref{container.reqmts}. + +\pnum +A program that instantiates +the definition of the template \tcode{indirect} +with a type for the \tcode{T} parameter that is +a non-object type, +an array type, +\tcode{in_place_t}, +a specialization of \tcode{in_place_type_t}, or +a cv-qualified type is ill-formed. + +\pnum +The template parameter \tcode{T} of \tcode{indirect} may be an incomplete type. + +\pnum +The template parameter \tcode{Allocator} of \tcode{indirect} shall meet +the \oldconcept{Allocator} requirements. + +\pnum +If a program declares an explicit or partial specialization of \tcode{indirect}, +the behavior is undefined. + +\begin{codeblock} +namespace std { + template> + class @\libglobal{indirect}@ { + public: + using value_type = T; + using allocator_type = Allocator; + using pointer = typename allocator_traits::pointer; + using const_pointer = typename allocator_traits::const_pointer; + + // \ref{indirect.ctor}, constructors + constexpr indirect(); + constexpr explicit indirect(allocator_arg_t, const Allocator& a); + constexpr indirect(const indirect& other); + constexpr indirect(allocator_arg_t, const Allocator& a, const indirect& other); + constexpr indirect(indirect&& other) noexcept; + constexpr indirect(allocator_arg_t, const Allocator& a, indirect&& other) + noexcept(@\seebelow@); + template + constexpr explicit indirect(U&& u); + template + constexpr explicit indirect(allocator_arg_t, const Allocator& a, U&& u); + template + constexpr explicit indirect(in_place_t, Us&&... us); + template + constexpr explicit indirect(allocator_arg_t, const Allocator& a, in_place_t, Us&&... us); + template + constexpr explicit indirect(in_place_t, initializer_list ilist, Us&&... us); + template + constexpr explicit indirect(allocator_arg_t, const Allocator& a, + in_place_t, initializer_list ilist, Us&&... us); + + // \ref{indirect.dtor}, destructor + constexpr ~indirect(); + + // \ref{indirect.assign}, assignment + constexpr indirect& operator=(const indirect& other); + constexpr indirect& operator=(indirect&& other) noexcept(@\seebelow@); + template + constexpr indirect& operator=(U&& u); + + // \ref{indirect.obs}, observers + constexpr const T& operator*() const & noexcept; + constexpr T& operator*() & noexcept; + constexpr const T&& operator*() const && noexcept; + constexpr T&& operator*() && noexcept; + constexpr const_pointer operator->() const noexcept; + constexpr pointer operator->() noexcept; + constexpr bool valueless_after_move() const noexcept; + constexpr allocator_type get_allocator() const noexcept; + + // \ref{indirect.swap}, swap + constexpr void swap(indirect& other) noexcept(@\seebelow@); + friend constexpr void swap(indirect& x, indirect& y) noexcept(@\seebelow@); + + // \ref{indirect.cmp}, comparisons + template + friend constexpr bool operator==(const indirect& x, const indirect& y) + noexcept(@\seebelow@); + template + friend constexpr bool operator==(const indirect& x, const U& y) noexcept(@\seebelow@); + template + friend constexpr auto operator<=>(const indirect& x, const indirect& y) + noexcept(@\seebelow@) -> compare_three_way_result_t; + template + friend constexpr auto operator<=>(const indirect& x, const U& y) noexcept(@\seebelow@) + -> compare_three_way_result_t; + private: + pointer @\exposid{p}@; // \expos + Allocator @\exposid{alloc}@ = Allocator(); // \expos + }; + + template + indirect(Value) -> indirect; + template + indirect(allocator_arg_t, Allocator, Value) + -> indirect::template rebind_alloc>; +} +\end{codeblock} + +\rSec2[indirect.ctor]{Constructors} + +\pnum +The following element applies to all functions in \ref{indirect.ctor}: + +\pnum +\throws +Nothing unless \tcode{allocator_traits::allocate} or +\tcode{allocator_traits::\linebreak construct} throws. + +\indexlibraryctor{indirect}% +\begin{itemdecl} +constexpr explicit indirect(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_default_constructible_v }is \tcode{true}, +\item \tcode{is_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_default_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +Constructs an owned object of type \tcode{T} with an empty argument list, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +constexpr explicit indirect(allocator_arg_t, const Allocator& a); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_default_constructible_v} is \tcode{true} and +\item \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +Constructs an owned object of type \tcode{T} with an empty argument list, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +constexpr indirect(const indirect& other); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete type and +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with +\tcode{allocator_traits::select_on_contai\-ner_copy_construction(other.\exposid{alloc})}. +If \tcode{other} is valueless, \tcode{*this} is valueless. +Otherwise, constructs an owned object of type \tcode{T} with \tcode{*other}, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +constexpr indirect(allocator_arg_t, const Allocator& a, const indirect& other); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete type and +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +If other is valueless, \tcode{*this} is valueless. +Otherwise, constructs an owned object of type \tcode{T} with \tcode{*other}, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +constexpr indirect(indirect&& other) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized +from \tcode{std::move(other.\exposid{alloc})}. +If \tcode{other} is valueless, \tcode{*this} is valueless. +Otherwise \tcode{*this }takes ownership of the owned object of \tcode{other}. + +\pnum +\ensures +\tcode{other} is valueless. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +constexpr indirect(allocator_arg_t, const Allocator& a, indirect&& other) + noexcept(allocator_traits::is_always_equal::value); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +If \tcode{allocator_traits::is_always_equal::value} is \tcode{false}, +then \tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +If \tcode{other} is valueless, \tcode{*this} is valueless. +Otherwise, if \tcode{\exposid{alloc} == other.\exposid{alloc}} is \tcode{true}, +constructs an object of type \tcode{indirect} +that takes ownership of the owned object of \tcode{other}. +Otherwise, constructs an owned object of type \tcode{T} +with \tcode{*std::move(other)}, +using the allocator \exposid{alloc}. + +\pnum +\ensures +\tcode{other} is valueless. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +template +constexpr explicit indirect(U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +Where \tcode{UU} is \tcode{remove_cvref_t}, +\begin{itemize} +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{is_constructible_v} is \tcode{true}, +\item \tcode{is_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_default_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +Constructs an owned object of type \tcode{T} with \tcode{std::forward(u)}, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +template +constexpr explicit indirect(allocator_arg_t, const Allocator& a, U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +Where \tcode{UU} is \tcode{remove_cvref_t}, +\begin{itemize} +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +Constructs an owned object of type \tcode{T} with \tcode{std::forward(u)}, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +template +constexpr explicit indirect(in_place_t, Us&&... us); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_constructible_v} is \tcode{true}, +\item \tcode{is_copy_constructible_v is true}, and +\item \tcode{is_default_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +Constructs an owned object of type \tcode{T} +with \tcode{std::forward(us)...}, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +template +constexpr explicit indirect(allocator_arg_t, const Allocator& a, in_place_t, Us&& ...us); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_constructible_v} is \tcode{true} and +\item \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +Constructs an owned object of type \tcode{T} +with \tcode{std::forward(us)...}, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +template +constexpr explicit indirect(in_place_t, initializer_list ilist, Us&&... us); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_copy_constructible_v} is \tcode{true}, +\item \tcode{is_constructible_v\&, Us...>} is \tcode{true}, and +\item \tcode{is_default_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +Constructs an owned object of type \tcode{T} with +the arguments \tcode{ilist}, \tcode{std::forward(us)...}, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{indirect}% +\begin{itemdecl} +template +constexpr explicit indirect(allocator_arg_t, const Allocator& a, + in_place_t, initializer_list ilist, Us&&... us); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_copy_constructible_v }is \tcode{true} and +\item \tcode{is_constructible_v\&, Us...>} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +Constructs an owned object of type \tcode{T} with the arguments +\tcode{ilist}, \tcode{std::forward(us)...}, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\rSec2[indirect.dtor]{Destructor} + +\indexlibrarydtor{indirect}% +\begin{itemdecl} +constexpr ~indirect(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +If \tcode{*this} is not valueless, +destroys the owned object using \tcode{allocator_traits::de\-stroy} +and then the storage is deallocated. +\end{itemdescr} + +\rSec2[indirect.assign]{Assignment} + +\indexlibrarymember{operator=}{indirect}% +\begin{itemdecl} +constexpr indirect& operator=(const indirect& other); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete type, +\tcode{is_copy_assignable_v} is \tcode{true}, and +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{addressof(other) == this} is \tcode{true}, there are no effects. +Otherwise: +\begin{itemize} +\item +The allocator needs updating if +\tcode{allocator_traits::propagate_on_container_copy_assignment::value} +is \tcode{true}. +\item +If \tcode{other} is valueless, \tcode{*this} becomes valueless and +the owned object in \tcode{*this}, if any, is destroyed +using \tcode{allocator_traits::destroy} and then +the storage is deallocated. +\item +Otherwise, +if \tcode{\exposid{alloc} == other.\exposid{alloc}} is \tcode{true} and +\tcode{*this} is not valueless, +equivalent to \tcode{**this = *other}. +\item +Otherwise a new owned object is constructed in \tcode{*this} +using \tcode{allocator_traits::\linebreak construct} +with the owned object from \tcode{other} as the argument, +using either the allocator in \tcode{*this} or +the allocator in \tcode{other} if the allocator needs updating. +\item +The previously owned object in \tcode{*this}, if any, is destroyed +using \tcode{allocator_traits::\linebreak destroy} +and then the storage is deallocated. +\item +If the allocator needs updating, +the allocator in \tcode{*this} is replaced with +a copy of the allocator in \tcode{other}. +\end{itemize} + +\pnum +\returns +A reference to \tcode{*this}. + +\pnum +\remarks +If any exception is thrown, +the result of the expression \tcode{this->valueless_after_move()} +remains unchanged. +If an exception is thrown +during the call to \tcode{T}'s selected 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. +\end{itemdescr} + +\indexlibrarymember{operator=}{indirect}% +\begin{itemdecl} +constexpr indirect& operator=(indirect&& other) + noexcept(allocator_traits::propagate_on_container_move_assignment::value || + allocator_traits::is_always_equal::value); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete type and +\tcode{is_copy_constructible_t} is \tcode{true}. + +\pnum +\effects +If \tcode{addressof(other) == this} is \tcode{true}, there are no effects. +Otherwise: +\begin{itemize} +\item +The allocator needs updating if +\tcode{allocator_traits::propagate_on_container_move_assignment::value} +is \tcode{true}. +\item +If \tcode{other} is valueless, +\tcode{*this} becomes valueless and +the owned object in \tcode{*this}, if any, is destroyed +using \tcode{allocator_traits::destroy} and then +the storage is deallocated. +\item +Otherwise, if \tcode{\exposid{alloc} == other.\exposid{alloc}} is \tcode{true}, +swaps the owned objects in \tcode{*this} and \tcode{other}; +the owned object in \tcode{other}, if any, is then destroyed +using \tcode{allocator_traits::destroy} and then +the storage is deallocated. +\item +Otherwise constructs a new owned object +with the owned object of \tcode{other} as the argument as an rvalue, +using either the allocator in \tcode{*this} or +the allocator in \tcode{other} if the allocator needs updating. +\item +The previously owned object in \tcode{*this}, if any, is destroyed +using \tcode{allocator_traits::\linebreak destroy} and then +the storage is deallocated. +\item +If the allocator needs updating, +the allocator in \tcode{*this} is replaced with +a copy of the allocator in \tcode{other}. +\end{itemize} + +\pnum +\ensures +\tcode{other} is valueless. + +\pnum +\returns +A reference to \tcode{*this.} + +\pnum +\remarks +If any exception is thrown, +there are no effects on \tcode{*this} or \tcode{other}. +\end{itemdescr} + +\indexlibrarymember{operator=}{indirect}% +\begin{itemdecl} +template +constexpr indirect& operator=(U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +Where \tcode{UU} is \tcode{remove_cvref_t}, +\begin{itemize} +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{is_assignable_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type and +\tcode{is_copy_constructible_t} is \tcode{true}. + +\pnum +\effects +If \tcode{*this} is valueless, then equivalent to +\begin{codeblock} +*this = indirect(allocator_arg, alloc, std::forward(u)); +\end{codeblock} +Otherwise, equivalent to \tcode{**this = std::forward(u)}. + +\pnum +\returns +A reference to \tcode{*this}. +\end{itemdescr} + +\rSec2[indirect.obs]{Observers} + +\indexlibrarymember{operator*}{indirect}% +\begin{itemdecl} +constexpr const T& operator*() const & noexcept; +constexpr T& operator*() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{*this} is not valueless. + +\pnum +\returns +\tcode{*\exposid{p}}. +\end{itemdescr} + +\indexlibrarymember{operator*}{indirect}% +\begin{itemdecl} +constexpr const T&& operator*() const && noexcept; +constexpr T&& operator*() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{*this} is not valueless. + +\pnum +\returns +\tcode{std::move(*\exposid{p})}. +\end{itemdescr} + +\indexlibrarymember{operator->}{indirect}% +\begin{itemdecl} +constexpr const_pointer operator->() const noexcept; +constexpr pointer operator->() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{*this} is not valueless. + +\pnum +\returns +\exposid{p}. +\end{itemdescr} + +\indexlibrarymember{valueless_after_move}{indirect}% +\begin{itemdecl} +constexpr bool valueless_after_move() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\tcode{true} if \tcode{*this} is valueless, otherwise \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{get_allocator}{indirect}% +\begin{itemdecl} +constexpr allocator_type get_allocator() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{alloc}. +\end{itemdescr} + +\rSec2[indirect.swap]{Swap} + +\indexlibrarymember{swap}{indirect}% +\begin{itemdecl} +constexpr void swap(indirect& other) + noexcept(allocator_traits::propagate_on_container_swap::value || + allocator_traits::is_always_equal::value); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +If +\tcode{allocator_traits::propagate_on_container_swap::value} +is \tcode{true}, +then \tcode{Allocator} meets the \oldconcept{Swappable} requirements. +Otherwise, +\tcode{get_allocator() == other.\linebreak get_allocator()} is \tcode{true}. + +\pnum +\effects +Swaps the states of \tcode{*this} and \tcode{other}, +exchanging owned objects or valueless states. +If \tcode{allocator_traits::propagate_on_container_swap::value} +is \tcode{true}, +then the allocators of \tcode{*this} and \tcode{other} are exchanged +by calling \tcode{swap} as described in \ref{swappable.requirements}. +Otherwise, the allocators are not swapped. +\begin{note} +The function \tcode{swap} is not invoked for the owned objects. +\end{note} +\end{itemdescr} + +\indexlibrarymember{swap}{indirect}% +\begin{itemdecl} +constexpr void swap(indirect& x, indirect& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec2[indirect.cmp]{Comparisons} + +\indexlibrarymember{operator==}{indirect}% +\begin{itemdecl} +template +constexpr bool operator==(const indirect& x, const indirect& y) + noexcept(noexcept(*x == *y)); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{*x == *y} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x} is valueless or \tcode{y} is valueless, +\tcode{x.valueless_after_move() == y.valueless_after_move()}; +otherwise \tcode{*x == *y}. +\end{itemdescr} + +\indexlibrarymember{operator<=>}{indirect}% +\begin{itemdecl} +template +constexpr @\exposid{synth-three-way-result}@ operator<=>(const indirect& x, const indirect& y) + noexcept(noexcept(@\exposid{synth-three-way}@(*x, *y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{*x <=> *y} is well-formed. + +\pnum +\returns +If \tcode{x} is valueless or \tcode{y} is valueless, +\tcode{!x.valueless_after_move() <=> !y.valueless_after_move()}; +otherwise \tcode{\exposid{synth-three-way}(*x, *y)}. +\end{itemdescr} + +\indexlibrarymember{operator==}{indirect}% +\begin{itemdecl} +template +constexpr bool operator==(const indirect& x, const U& y) noexcept(noexcept(*x == y)); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{*x == y} is well-formed. + +\pnum +\returns +If \tcode{x} is valueless, \tcode{false}; otherwise \tcode{*x == y}. +\end{itemdescr} + +\indexlibrarymember{operator<=>}{indirect}% +\begin{itemdecl} +template +constexpr @\exposid{synth-three-way-result}@ operator<=>(const indirect& x, const U& y) + noexcept(noexcept(@\exposid{synth-three-way}@(*x, y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{*x <=> y} is well-formed. + +\pnum +\returns +If \tcode{x} is valueless, \tcode{false < true}; +otherwise \tcode{\exposid{synth-three-way}(*x, y)}. +\end{itemdescr} + +\rSec2[indirect.hash]{Hash support} + +\indexlibrarymember{hash}{indirect}% +\begin{itemdecl} +template +struct hash>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +The specialization \tcode{hash>} +is enabled\iref{unord.hash} +if and only if \tcode{hash} is enabled. +When enabled for an object \tcode{i} of type \tcode{indirect}, +then \tcode{hash>()(i)} +evaluates to either the same value as \tcode{hash()(*i)}, +if \tcode{i} is not valueless; +otherwise to +an \impldef{value of \tcode{hash>} for valueless object} value. +The member functions are not guaranteed to be \tcode{noexcept}. +\end{itemdescr} + +\rSec1[polymorphic]{Class template \tcode{polymorphic}} + +\rSec2[polymorphic.general]{General} + +\pnum +A polymorphic object manages the lifetime of an owned object. +A polymorphic object may own objects of different types +at different points in its lifetime. +A polymorphic object is \defnx{valueless}{valueless!polymorphic object} +if it has no owned object. +A polymorphic object may only become valueless after it has been moved from. + +\pnum +In every specialization \tcode{polymorphic}, +if the type \tcode{allocator_traits::value_type} +is not the same type as \tcode{T}, the program is ill-formed. +Every object of type \tcode{polymorphic} +uses an object of type \tcode{Allocator} +to allocate and free storage for the owned object as needed. + +\pnum +Constructing an owned object of type \tcode{U} with \tcode{args...} +using the allocator \tcode{a} means +calling \tcode{allocator_traits::construct(p, args...)} +where \tcode{args} is an expression pack, +\tcode{a} is an allocator, and +\tcode{p} points to storage suitable for an owned object of type \tcode{U}. + +\pnum +The member \exposid{alloc} is used for +any memory allocation and element construction performed by member functions +during the lifetime of each polymorphic value object, or +until the allocator is replaced. +The allocator may only be replaced via assignment or \tcode{swap}. +Allocator replacement is performed by +copy assignment, move assignment, or swapping of the allocator +only if +\begin{itemize} +\item +\tcode{allocator_traits::propagate_on_container_copy_assignment::value}, +\item +\tcode{allocator_traits::propagate_on_container_move_assignment::value}, or +\item +\tcode{allocator_traits::propagate_on_container_swap::value} +\end{itemize} +is \tcode{true} within the implementation of +the corresponding polymorphic operation\iref{container.reqmts}. + +\pnum +A program that instantiates the definition of \tcode{polymorphic} for +a non-object type, +an array type, +\tcode{in_place_t}, +a specialization of \tcode{in_place_type_t}, or +a cv-qualified type +is ill-formed. + +\pnum +The template parameter \tcode{T} of polymorphic may be an incomplete type. + +\pnum +The template parameter \tcode{Allocator} of \tcode{polymorphic} +shall meet the requirements of \oldconcept{Allocator}. + +\pnum +If a program declares +an explicit or partial specialization of \tcode{polymorphic}, +the behavior is undefined. + +\begin{codeblock} +namespace std { + template> + class @\libglobal{polymorphic}@ { + public: + using value_type = T; + using allocator_type = Allocator; + using pointer = typename allocator_traits::pointer; + using const_pointer = typename allocator_traits::const_pointer; + + // \ref{polymorphic.ctor}, constructors + constexpr explicit polymorphic(); + constexpr explicit polymorphic(allocator_arg_t, const Allocator& a); + constexpr polymorphic(const polymorphic& other); + constexpr polymorphic(allocator_arg_t, const Allocator& a, const polymorphic& other); + constexpr polymorphic(polymorphic&& other) noexcept; + constexpr polymorphic(allocator_arg_t, const Allocator& a, + polymorphic&& other) noexcept(@\seebelow@); + template + constexpr explicit polymorphic(U&& u); + template + constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, U&& u); + template + constexpr explicit polymorphic(in_place_type_t, Ts&&... ts); + template + constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, + in_place_type_t, Ts&&... ts); + template + constexpr explicit polymorphic(in_place_type_t, initializer_list ilist, Us&&... us); + template + constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, + in_place_type_t, initializer_list ilist, Us&&... us); + + // \ref{polymorphic.dtor}, destructor + constexpr ~polymorphic(); + + // \ref{polymorphic.assign}, assignment + constexpr polymorphic& operator=(const polymorphic& other); + constexpr polymorphic& operator=(polymorphic&& other) noexcept(@\seebelow@); + + // \ref{polymorphic.obs}, observers + constexpr const T& operator*() const noexcept; + constexpr T& operator*() noexcept; + constexpr const_pointer operator->() const noexcept; + constexpr pointer operator->() noexcept; + constexpr bool valueless_after_move() const noexcept; + constexpr allocator_type get_allocator() const noexcept; + + // \ref{polymorphic.swap}, swap + constexpr void swap(polymorphic& other) noexcept(@\seebelow@); + friend constexpr void swap(polymorphic& x, polymorphic& y) noexcept(@\seebelow@); + private: + Allocator alloc = Allocator(); // \expos + }; +} +\end{codeblock} + +\rSec2[polymorphic.ctor]{Constructors} + +\pnum +The following element applies to all functions in \ref{polymorphic.ctor}: + +\pnum +\throws +Nothing unless \tcode{allocator_traits::allocate} +or \tcode{allocator_traits::\linebreak construct} throws. + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +constexpr explicit polymorphic(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_default_constructible_v} is \tcode{true}, +\item \tcode{is_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_default_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +Constructs an owned object of type \tcode{T} with an empty argument list +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +constexpr explicit polymorphic(allocator_arg_t, const Allocator& a); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_default_constructible_v} is \tcode{true} and +\item \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +Constructs an owned object of type \tcode{T} with an empty argument list +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +constexpr polymorphic(const polymorphic& other); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with +\tcode{allocator_traits::select_on_contai\-ner_copy_construction(other.\exposid{alloc})}. +If \tcode{other} is valueless, \tcode{*this} is valueless. +Otherwise, constructs an owned object of type \tcode{U}, +where \tcode{U} is the type of the owned object in \tcode{other}, +with the owned object in \tcode{other}, using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +constexpr polymorphic(allocator_arg_t, const Allocator& a, const polymorphic& other); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \exposid{alloc}. +If \tcode{other} is valueless, \tcode{*this} is valueless. +Otherwise, constructs an owned object of type \tcode{U}, +where \tcode{U} is the type of the owned object in \tcode{other}, +with the owned object in \tcode{other}, using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +constexpr polymorphic(polymorphic&& other) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{std::move(other.alloc)}. +If \tcode{other} is valueless, \tcode{*this} is valueless. +Otherwise, +either \tcode{*this} takes ownership of the owned object of \tcode{other} or +owns an object of the same type +constructed from the owned object of \tcode{other} +considering that owned object as an rvalue, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +constexpr polymorphic(allocator_arg_t, const Allocator& a, polymorphic&& other) + noexcept(allocator_traits::is_always_equal::value); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +If \tcode{other} is valueless, \tcode{*this} is valueless. +Otherwise, if \tcode{\exposid{alloc} == other.\exposid{alloc} }is \tcode{true}, +either constructs an object of type \tcode{polymorphic} +that owns the owned object of \tcode{other}, +making \tcode{other} valueless or +owns an object of the same type constructed from +the owned object of \tcode{other} +considering that owned object as an rvalue. +Otherwise, if \tcode{\exposid{alloc} != other.\exposid{alloc}} is \tcode{true}, +constructs an object of type \tcode{polymorphic}, +considering the owned object in \tcode{other} as an rvalue, +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +template +constexpr explicit polymorphic(U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +Where \tcode{UU} is \tcode{remove_cvref_t}, +\begin{itemize} +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{derived_from} is \tcode{true}, +\item \tcode{is_copy_constructible_v} is \tcode{true}, +\item \tcode{is_constructible_v} is \tcode{true}, +\item \tcode{UU} is not a specialization of \tcode{in_place_type_t}, and +\item \tcode{is_default_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +Constructs an owned object of type \tcode{U} with \tcode{std::forward(u)} +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +template +constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +Where \tcode{UU} is \tcode{remove_cvref_t}, +\begin{itemize} +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{derived_from is \tcode{true}, +\item \tcode{is_copy_constructible_v} is \tcode{true}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{UU} is not a specialization of \tcode{in_place_type_t}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +Constructs an owned object of type \tcode{U} with \tcode{std::forward(u)} +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +template +constexpr explicit polymorphic(in_place_type_t, Ts&&... ts); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, U>} is \tcode{true}, +\item \tcode{derived_from} is \tcode{true}, +\item \tcode{is_constructible_v} is \tcode{true}, +\item \tcode{is_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_default_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +Constructs an owned object of type \tcode{U} +with \tcode{std::forward(ts)...} using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +template +constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, + in_place_type_t, Ts&&... ts); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, U>} is \tcode{true}, +\item \tcode{derived_from} is \tcode{true}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +Constructs an owned object of type \tcode{U} +with \tcode{std::forward(ts)...} using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +template +constexpr explicit polymorphic(in_place_type_t, initializer_list ilist, Us&&... us); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, U>} is \tcode{true}, +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{derived_from} is \tcode{true}, +\item \tcode{is_copy_constructible_v} is \tcode{true}, +\item \tcode{is_constructible_v\&, Us...>} is \tcode{true}, and +\item \tcode{is_default_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +Constructs an owned object of type \tcode{U} +with the arguments \tcode{ilist}, \tcode{std::forward(us)...} +using the allocator \exposid{alloc}. +\end{itemdescr} + +\indexlibraryctor{polymorphic}% +\begin{itemdecl} +template +constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, + in_place_type_t, initializer_list ilist, Us&&... us); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, U>} is \tcode{true}, +\item \tcode{is_same_v} is \tcode{false}, +\item \tcode{derived_from} is \tcode{true}, +\item \tcode{is_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_constructible_v\&, Us...>} is \tcode{true}. +\end{itemize} + +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +\exposid{alloc} is direct-non-list-initialized with \tcode{a}. +Constructs an owned object of type \tcode{U} +with the arguments \tcode{ilist}, \tcode{std::forward(us)...} +using the allocator \exposid{alloc}. +\end{itemdescr} + +\rSec2[polymorphic.dtor]{Destructor} + +\indexlibrarydtor{polymorphic}% +\begin{itemdecl} +constexpr ~polymorphic(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +If \tcode{*this} is not valueless, +destroys the owned object using \tcode{allocator_traits::\linebreak destroy} +and then the storage is deallocated. +\end{itemdescr} + +\rSec2[polymorphic.assign]{Assignment} + +\indexlibrarymember{operator=}{polymorphic}% +\begin{itemdecl} +constexpr polymorphic& operator=(const polymorphic& other); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete type. + +\pnum +\effects +If \tcode{addressof(other) == this} is \tcode{true}, there are no effects. +Otherwise: +\begin{itemize} +\item +The allocator needs updating if +\tcode{allocator_traits::propagate_on_container_copy_assignment::value} +is \tcode{true}. +\item +If \tcode{other} is not valueless, +a new owned object is constructed in \tcode{*this} +using \tcode{allocator_traits::construct} +with the owned object from \tcode{other} as the argument, +using either the allocator in \tcode{*this} or +the allocator in \tcode{other} if the allocator needs updating. +\item +The previously owned object in \tcode{*this}, if any, is destroyed +using \tcode{allocator_traits::\linebreak destroy} +and then the storage is deallocated. +\item +If the allocator needs updating, +the allocator in \tcode{*this} is replaced with +a copy of the allocator in \tcode{other}. +\end{itemize} + +\pnum +\returns +A reference to \tcode{*this}. + +\pnum +\remarks +If any exception is thrown, there are no effects on \tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{polymorphic}% +\begin{itemdecl} +constexpr polymorphic& operator=(polymorphic&& other) + noexcept(allocator_traits::propagate_on_container_move_assignment::value || + allocator_traits::is_always_equal::value); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +If \tcode{allocator_traits::is_always_equal::value} is \tcode{false}, +\tcode{T} is a complete type. + +\pnum +\effects +If \tcode{addressof(other) == this} is \tcode{true}, there are no effects. +Otherwise: +\begin{itemize} +\item +The allocator needs updating if +\tcode{allocator_traits::propagate_on_container_move_assignment::value} +is \tcode{true}. +\item +If \tcode{\exposid{alloc} == other.\exposid{alloc}} is \tcode{true}, +swaps the owned objects in \tcode{*this} and \tcode{other}; +the owned object in \tcode{other}, if any, is then destroyed +using \tcode{allocator_traits::destroy} and then +the storage is deallocated. +\item +Otherwise, if \tcode{\exposid{alloc} != other.\exposid{alloc}} is \tcode{true}; +if \tcode{other} is not valueless, a new owned object is constructed in +\tcode{*this} using \tcode{allocator_traits::construct} +with the owned object from \tcode{other} as the argument as an rvalue, +using either the allocator in \tcode{*this} or +the allocator in \tcode{other} if the allocator needs updating. +\item +The previously owned object in \tcode{*this}, if any, is destroyed +using \tcode{allocator_traits::\linebreak destroy} +and then the storage is deallocated. +\item +If the allocator needs updating, +the allocator in \tcode{*this} is replaced with +a copy of the allocator in \tcode{other}. +\end{itemize} + +\pnum +\returns +A reference to \tcode{*this}. + +\pnum +\remarks +If any exception is thrown, +there are no effects on \tcode{*this} or \tcode{other}. +\end{itemdescr} + +\rSec2[polymorphic.obs]{Observers} + +\indexlibrarymember{operator*}{polymorphic}% +\begin{itemdecl} +constexpr const T& operator*() const noexcept; +constexpr T& operator*() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{*this} is not valueless. + +\pnum +\returns +A reference to the owned object. +\end{itemdescr} + +\indexlibrarymember{operator->}{polymorphic}% +\begin{itemdecl} +constexpr const_pointer operator->() const noexcept; +constexpr pointer operator->() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{*this} is not valueless. + +\pnum +\returns +A pointer to the owned object. +\end{itemdescr} + +\indexlibrarymember{valueless_after_move}{polymorphic}% +\begin{itemdecl} +constexpr bool valueless_after_move() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{true} if \tcode{*this} is valueless, otherwise \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{get_allocator}{polymorphic}% +\begin{itemdecl} +constexpr allocator_type get_allocator() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{alloc}. +\end{itemdescr} + +\rSec2[polymorphic.swap]{Swap} + +\indexlibrarymember{swap}{polymorphic}% +\begin{itemdecl} +constexpr void swap(polymorphic& other) + noexcept(allocator_traits::propagate_on_container_swap::value || + allocator_traits::is_always_equal::value); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +If +\tcode{allocator_traits::propagate_on_container_swap::value} +is \tcode{true}, +then \tcode{Allocator} meets the \oldconcept{Swappable} requirements. +Otherwise, +\tcode{get_allocator() == other.\linebreak get_allocator()} is \tcode{true}. + +\pnum +\effects +Swaps the states of \tcode{*this} and \tcode{other}, +exchanging owned objects or valueless states. +If \tcode{allocator_traits::propagate_on_container_swap::value} +is \tcode{true}, +then the allocators of \tcode{*this} and other are exchanged +by calling \tcode{swap} as described in \ref{swappable.requirements}. +Otherwise, the allocators are not swapped. +\begin{note} +The function \tcode{swap} is not invoked for the owned objects. +\end{note} +\end{itemdescr} + +\indexlibrarymember{swap}{polymorphic}% +\begin{itemdecl} +constexpr void swap(polymorphic& x, polymorphic& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} diff --git a/source/support.tex b/source/support.tex index 28c15ac913..ff50f96022 100644 --- a/source/support.tex +++ b/source/support.tex @@ -681,6 +681,7 @@ #define @\defnlibxname{cpp_lib_hypot}@ 201603L // also in \libheader{cmath} #define @\defnlibxname{cpp_lib_incomplete_container_elements}@ 201505L // also in \libheader{forward_list}, \libheader{list}, \libheader{vector} +#define @\defnlibxname{cpp_lib_indirect}@ 202411L // also in \libheader{memory} #define @\defnlibxname{cpp_lib_inplace_vector}@ 202406L // also in \libheader{inplace_vector} #define @\defnlibxname{cpp_lib_int_pow2}@ 202002L // freestanding, also in \libheader{bit} #define @\defnlibxname{cpp_lib_integer_comparison_functions}@ 202002L // also in \libheader{utility} @@ -732,6 +733,7 @@ #define @\defnlibxname{cpp_lib_out_ptr}@ 202311L // freestanding, also in \libheader{memory} #define @\defnlibxname{cpp_lib_parallel_algorithm}@ 201603L // also in \libheader{algorithm}, \libheader{numeric} #define @\defnlibxname{cpp_lib_philox_engine}@ 202406L // also in \libheader{random} +#define @\defnlibxname{cpp_lib_polymorphic}@ 202411L // also in \libheader{memory} #define @\defnlibxname{cpp_lib_polymorphic_allocator}@ 201902L // also in \libheader{memory_resource} #define @\defnlibxname{cpp_lib_print}@ 202406L // also in \libheader{print}, \libheader{ostream} #define @\defnlibxname{cpp_lib_quoted_string_io}@ 201304L // also in \libheader{iomanip}