diff --git a/.gitignore b/.gitignore index ad9eb3e4..61358f4e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ compile_commands.json .update-submodules /.install/ /.venv/ +/papers/P2988/_minted/ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 79322464..8650adb6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -9,6 +9,7 @@ include(GNUInstallDirs) # List of all buildable examples. set(EXAMPLES + base_derived_cast concept_checks optional_ref pythagorean_triples diff --git a/examples/base_derived_cast.cpp b/examples/base_derived_cast.cpp new file mode 100644 index 00000000..4f5d0057 --- /dev/null +++ b/examples/base_derived_cast.cpp @@ -0,0 +1,25 @@ +// examples/optional_ref.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +struct derived; +extern derived d; +struct base { + virtual ~base() = default; + operator derived&() { return d; } +}; + +struct derived : base {}; + +derived d; + +int example() { + base b; + derived& dref(b); // ok + beman::optional::optional dopt(b); // ok + (void)dref; + return 0; +} + +int main() { example(); } diff --git a/examples/optional_ref.cpp b/examples/optional_ref.cpp index bcfe5e1b..1c0bacbf 100644 --- a/examples/optional_ref.cpp +++ b/examples/optional_ref.cpp @@ -44,7 +44,7 @@ beman::optional::optional api() { } // namespace std26 -int example1() { +int example() { // Example from P2988R5: optional reference. [[maybe_unused]] Cat* old_cat = std17::api(); [[maybe_unused]] beman::optional::optional new_cat = std26::api(); @@ -52,28 +52,7 @@ int example1() { return 0; } -struct derived; -extern derived d; -struct base { - virtual ~base() = default; - operator derived&() { return d; } -}; - -struct derived : base {}; - -derived d; - -int example2() { - base b; - derived& dref(b); // ok - beman::optional::optional dopt(b); - return 0; -} - -int main() { - example1(); - example2(); -} +int main() { example(); } // # build example: // $ cmake --workflow --preset gcc-14 // diff --git a/include/beman/optional/optional.hpp b/include/beman/optional/optional.hpp index 1064d4e7..25e0bfe6 100644 --- a/include/beman/optional/optional.hpp +++ b/include/beman/optional/optional.hpp @@ -69,7 +69,7 @@ inline constexpr bool std::ranges::enable_view> = t // Iterators for optional have life times that are not tied to the optional. template -inline constexpr bool std::ranges::enable_borrowed_range> = std::is_reference_v; +inline constexpr bool std::ranges::enable_borrowed_range> = true; // Since P3168R2: Give std::optional Range Support. #if defined(__cpp_lib_format_ranges) @@ -699,21 +699,15 @@ inline constexpr bool optional::has_value() const noexcept { /// bad_optional_access template inline constexpr T& optional::value() & { - if (has_value()) - return value_; - throw bad_optional_access(); + return has_value() ? value_ : throw bad_optional_access(); } template inline constexpr const T& optional::value() const& { - if (has_value()) - return value_; - throw bad_optional_access(); + return has_value() ? value_ : throw bad_optional_access(); } template inline constexpr T&& optional::value() && { - if (has_value()) - return std::move(value_); - throw bad_optional_access(); + return has_value() ? std::move(value_) : throw bad_optional_access(); } /// Returns the stored value if there is one, otherwise returns `u` @@ -1090,8 +1084,8 @@ class optional { public: // \ref{optionalref.ctor}, constructors - constexpr optional() noexcept; - constexpr optional(nullopt_t) noexcept; + constexpr optional() noexcept = default; + constexpr optional(nullopt_t) noexcept : optional() {} constexpr optional(const optional& rhs) noexcept = default; template @@ -1102,12 +1096,9 @@ class optional { requires(std::is_constructible_v && !(std::is_same_v, in_place_t>) && !(std::is_same_v, optional>) && !detail::reference_constructs_from_temporary_v) - constexpr explicit(!std::is_convertible_v) optional(U&& u) noexcept( - std::is_nothrow_constructible_v) { // Creates a variable, \tcode{r}, as if by \tcode{T\& - // r(std::forward(arg));} and then initializes \exposid{val} - // with \tcode{addressof(r)} - T& r(std::forward(u)); - value_ = std::addressof(r); + constexpr explicit(!std::is_convertible_v) + optional(U&& u) noexcept(std::is_nothrow_constructible_v) { + convert_ref_init_val(u); } template @@ -1189,14 +1180,14 @@ class optional { constexpr explicit operator bool() const noexcept; constexpr bool has_value() const noexcept; constexpr T& value() const; - template + template > constexpr std::remove_cv_t value_or(U&& u) const; // \ref{optionalref.monadic}, monadic operations template constexpr auto and_then(F&& f) const; template - constexpr auto transform(F&& f) const -> optional>; + constexpr optional> transform(F&& f) const; template constexpr optional or_else(F&& f) const; @@ -1204,24 +1195,25 @@ class optional { constexpr void reset() noexcept; private: - T* value_; // exposition only + T* value_ = nullptr; // exposition only + + // \ref{optionalref.expos}, exposition only helper functions + template + constexpr void convert_ref_init_val(U&& u) { + // Creates a variable, \tcode{r}, + // as if by \tcode{T\& r(std::forward(u));} + // and then initializes \exposid{val} with \tcode{addressof(r)} + T& r(std::forward(u)); + value_ = std::addressof(r); + } }; // \rSec3[optionalref.ctor]{Constructors} -template -constexpr optional::optional() noexcept : value_(nullptr) {} - -template -constexpr optional::optional(nullopt_t) noexcept : value_(nullptr) {} - template template requires(std::is_constructible_v && !detail::reference_constructs_from_temporary_v) -constexpr optional::optional( - in_place_t, Arg&& arg) { // Creates a variable, \tcode{r}, as if by \tcode{T\& r(std::forward(arg));} and - // then initializes \exposid{val} with \tcode{addressof(r)} - T& r(std::forward(arg)); - value_ = std::addressof(r); +constexpr optional::optional(in_place_t, Arg&& arg) { + convert_ref_init_val(std::forward(arg)); } // Clang is unhappy with the out-of-line definition @@ -1239,9 +1231,7 @@ template !std::is_same_v && !detail::reference_constructs_from_temporary_v) constexpr optional::optional(optional& rhs) noexcept(std::is_nothrow_constructible_v) { if (rhs.has_value()) { - value_ = std::addressof(static_cast(*rhs)); - } else { - value_ = nullptr; + convert_ref_init_val(*rhs); } } @@ -1251,9 +1241,7 @@ template !std::is_same_v && !detail::reference_constructs_from_temporary_v) constexpr optional::optional(const optional& rhs) noexcept(std::is_nothrow_constructible_v) { if (rhs.has_value()) { - value_ = std::addressof(static_cast(*rhs)); - } else { - value_ = nullptr; + convert_ref_init_val(*rhs); } } @@ -1263,9 +1251,7 @@ template !std::is_same_v && !detail::reference_constructs_from_temporary_v) constexpr optional::optional(optional&& rhs) noexcept(noexcept(std::is_nothrow_constructible_v)) { if (rhs.has_value()) { - value_ = std::addressof(static_cast(*std::move(rhs))); - } else { - value_ = nullptr; + convert_ref_init_val(*std::move(rhs)); } } @@ -1276,9 +1262,7 @@ template constexpr optional::optional(const optional&& rhs) noexcept( noexcept(std::is_nothrow_constructible_v)) { if (rhs.has_value()) { - value_ = std::addressof(static_cast(*std::move(rhs))); - } else { - value_ = nullptr; + convert_ref_init_val(*std::move(rhs)); } } @@ -1293,7 +1277,7 @@ template template requires(std::is_constructible_v && !detail::reference_constructs_from_temporary_v) constexpr T& optional::emplace(U&& u) noexcept(std::is_nothrow_constructible_v) { - value_ = std::addressof(static_cast(std::forward(u))); + convert_ref_init_val(std::forward(u)); return *value_; } @@ -1338,9 +1322,7 @@ constexpr bool optional::has_value() const noexcept { template constexpr T& optional::value() const { - if (has_value()) - return *value_; - throw bad_optional_access(); + return has_value() ? *value_ : throw bad_optional_access(); } template @@ -1348,11 +1330,7 @@ template constexpr std::remove_cv_t optional::value_or(U&& u) const { static_assert(std::is_constructible_v, T&>, "T must be constructible from a T&"); static_assert(std::is_convertible_v>, "Must be able to convert u to T"); - if (has_value()) { - return std::remove_cv_t(*value_); - } else { - return std::forward(u); - } + return has_value() ? *value_ : static_cast>(std::forward(u)); } // \rSec3[optionalref.monadic]{Monadic operations} @@ -1370,7 +1348,7 @@ constexpr auto optional::and_then(F&& f) const { template template -constexpr auto optional::transform(F&& f) const -> optional> { +constexpr optional> optional::transform(F&& f) const { using U = std::invoke_result_t; static_assert(!std::is_same_v, in_place_t>, "Result must not be in_place_t"); static_assert(!std::is_same_v, nullopt_t>, "Result must not be nullopt_t"); @@ -1387,7 +1365,7 @@ template template constexpr optional optional::or_else(F&& f) const { using U = std::invoke_result_t; - static_assert(std::is_same_v, optional>); + static_assert(std::is_same_v, optional>, "Result must be an optional"); if (has_value()) { return *value_; } else { diff --git a/lwg-20250314.org b/lwg-20250314.org new file mode 100644 index 00000000..05263606 --- /dev/null +++ b/lwg-20250314.org @@ -0,0 +1,102 @@ +#+options: ':nil *:t -:t ::t <:t H:8 \n:nil ^:t arch:headline author:t +#+options: broken-links:nil c:nil creator:nil d:(not "LOGBOOK") date:t e:t +#+options: email:nil expand-links:t f:t inline:t num:t p:nil pri:nil prop:nil +#+options: stat:t tags:t tasks:t tex:t timestamp:t title:t toc:t todo:t |:t +#+title: lwg-20250314 +#+date: <2025-03-14 Fri> +#+author: Steve Downey +#+email: sdowney@pwyll +#+language: en +#+select_tags: export +#+exclude_tags: noexport +#+creator: Emacs 30.1.50 (Org mode 9.7.11) +#+cite_export: + +#+options: html-link-use-abs-url:nil html-postamble:auto html-preamble:t +#+options: html-scripts:t html-style:t html5-fancy:nil tex:t +#+html_doctype: xhtml-strict +#+html_container: div +#+html_content_class: content +#+description: +#+keywords: +#+html_link_home: +#+html_link_up: +#+html_mathjax: +#+html_equation_reference_format: \eqref{%s} +#+html_head: +#+html_head_extra: +#+subtitle: +#+infojs_opt: +#+creator: Emacs 30.1.50 (Org mode 9.7.11) +#+latex_header: + +* Notes +https://wg21.link/P2988r9 std::optional +SD presenting +[optionalref.iterators] +TS: p1: “These types” -> “This type” +DK: there is no const_iterator? +SD: no, constness is shallow +JW: should there be a const_iterator typedef? +TK: no, it would be a lie +JW: “referring to the referred to value” +TK: referring to *val +SD: I have “referring to the contained value” +[optionalref.observe] +SD: there should be hardened preconditions once that landed +JW: make value() a one-liner as in non-specialization +TK: value_or should remove_cv_t +TK: the mandate shouldn’t have a std:: qualification +JW: should the stable names use [optional.ref] +JW: also a one-liner on value_or +JW: we did something on value_or() to allow using {} as argument +JW: the Mandate on value_or() looks different +SD: over here it is a bit longer and we should probably fix the other one +[optionalref.monadics] +TK: in p3 it becomes valid contained type? +SD: yes +JW: in p7 what is U? There is something missing +TK: it should be invoke_result_t +TK: the definition of U should be consistent +JW: in p4: we shouldn’t use trailing return types here +TK: we can remove the mandates +SD: no, it is needed +JW: now that there is a “contains” we could write it exactly the same as normal optional +TK: we want to use the same wording for transform() +Stopping before [optionalref.mod] + +* Checklist +SD presenting +[optionalref.iterators] +- [X] TS: p1: “These types” -> “This type” +DK: there is no const_iterator? +SD: no, constness is shallow +JW: should there be a const_iterator typedef? +TK: no, it would be a lie +JW: “referring to the referred to value” +TK: referring to *val +SD: I have “referring to the contained value” +[optionalref.observe] +SD: there should be hardened preconditions once that landed +- [X] JW: make value() a one-liner as in non-specialization +- [X] TK: value_or should remove_cv_t +- [X] TK: the mandate shouldn’t have a std:: qualification +- [X] JW: should the stable names use [optional.ref] +- [X] JW: also a one-liner on value_or +- [X] JW: we did something on value_or() to allow using {} as argument +JW: the Mandate on value_or() looks different +SD: over here it is a bit longer and we should probably fix the other one +[optionalref.monadics] +TK: in p3 it becomes valid contained type? +SD: yes +- [X] JW: in p7 what is U? There is something missing +- [X] TK: it should be invoke_result_t +- [X] TK: the definition of U should be consistent +- [X] JW: in p4: we shouldn’t use trailing return types here +TK: we can remove the mandates +SD: no, it is needed +JW: now that there is a “contains” we could write it exactly the same as normal optional +- [X] TK: we want to use the same wording for transform() +Stopping before [optionalref.mod] + +Note add "contained value" definition for optional diff --git a/papers/P2988/base-optional.tex b/papers/P2988/base-optional.tex index 613c2689..34eba508 100644 --- a/papers/P2988/base-optional.tex +++ b/papers/P2988/base-optional.tex @@ -934,8 +934,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{*this} contains a value. +\hardexpects +\tcode{has_value()} is \tcode{true}. \pnum \returns @@ -954,8 +954,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{*this} contains a value. +\hardexpects +\tcode{has_value()} is \tcode{true}. \pnum \returns @@ -974,8 +974,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{*this} contains a value. +\hardexpects +\tcode{has_value()} is \tcode{true}. \pnum \effects @@ -1292,7 +1292,7 @@ class bad_optional_access : public exception { public: // see \ref{exception} for the specification of the special member functions - const char* what() const noexcept override; + constexpr const char* what() const noexcept override; }; } \end{codeblock} @@ -1302,13 +1302,15 @@ \indexlibrarymember{what}{bad_optional_access}% \begin{itemdecl} -const char* what() const noexcept override; +constexpr const char* what() const noexcept override; \end{itemdecl} \begin{itemdescr} \pnum \returns -An \impldef{return value of \tcode{bad_optional_access::what}} \ntbs{}. +An \impldef{return value of \tcode{bad_optional_access::what}} \ntbs{}, +which during constant evaluation is encoded with +the ordinary literal encoding\iref{lex.ccon}. \end{itemdescr} \rSec2[optional.relops]{Relational operators} @@ -1795,3 +1797,8 @@ otherwise it evaluates to an unspecified value. The member functions are not guaranteed to be \keyword{noexcept}. \end{itemdescr} + +%%% Local Variables: +%%% mode: LaTeX +%%% TeX-master: "base" +%%% End: diff --git a/papers/P2988/common.tex b/papers/P2988/common.tex index b85b3617..a794a41c 100644 --- a/papers/P2988/common.tex +++ b/papers/P2988/common.tex @@ -3,7 +3,7 @@ %%-------------------------------------------------- %% basics -% \documentclass[a4paper,11pt,oneside,openany,final,article]{memoir} +% \documentclass[a4paper,10pt,oneside,openany,final,article]{memoir} \usepackage[american] {babel} % needed for iso dates @@ -13,6 +13,7 @@ {listings} % code listings \usepackage{longtable} % auto-breaking tables \usepackage{ltcaption} % fix captions for long tables +\usepackage{caption} % caption style \usepackage{relsize} % provide relative font size changes \usepackage{textcomp} % provide \text{l,r}angle \usepackage{underscore} % remove special status of '_' in ordinary text @@ -76,24 +77,29 @@ \input{stdtex/tables} \input{stdtex/paper_macros} -% %% -------------------------------------------------- +% %%-------------------------------------------------- % %% fix interaction between hyperref and other % %% commands % \pdfstringdefDisableCommands{\def\smaller#1{#1}} % \pdfstringdefDisableCommands{\def\textbf#1{#1}} % \pdfstringdefDisableCommands{\def\raisebox#1{}} % \pdfstringdefDisableCommands{\def\hspace#1{}} +% \pdfstringdefDisableCommands{\def\frenchspacing{}} %%-------------------------------------------------- %% add special hyphenation rules -\hyphenation{tem-plate ex-am-ple in-put-it-er-a-tor name-space name-spaces non-zero} +\hyphenation{tem-plate ex-am-ple in-put-it-er-a-tor name-space name-spaces non-zero cus-tom-i-za-tion im-ple-men-ted} %%-------------------------------------------------- %% turn off all ligatures inside \texttt \DisableLigatures{encoding = T1, family = tt*} +%%-------------------------------------------------- +%% select regular text font for \url +\urlstyle{same} + %% -------------------------------------------------- %% configuration \input{stdtex/config} diff --git a/papers/P2988/ldiff.sh b/papers/P2988/ldiff.sh new file mode 100755 index 00000000..603e6e5b --- /dev/null +++ b/papers/P2988/ldiff.sh @@ -0,0 +1,7 @@ +#!/bin/env sh + +export SAFECMDS=Rplus,Cpp,CppIII,opt,shl,shr,dcr,exor,bigoh,tilde,bitand,bitor,xor,rightshift,enternote,enterexample,exitexample,required,requires,effects,postconditions,postcondition,preconditions,precondition,returns,throws,default,complexity,remark,remarks,note,notes,realnote,realnotes,errors,sync,implimits,replaceable,exceptionsafety,returntype,cvalue,ctype,ctypes,dtype,ctemplate,templalias,xref,xsee,ntbs,ntmbs,ntwcs,ntcxvis,ntcxxxiis,expos,impdef,notdef,unspec,unspecbool,seebelow,unspecuniqtype,unspecalloctype,unun,change,rationale,effect,difficulty,howwide,uniquens,cv,seebelow + +export TEXTCMDS=tcode,term,grammarterm,techterm,defnx,defn,Fundescx,Fundesc,state,leftshift,EnterBlock,ExitBlock,NTS,EXPO,impdefx,UNSP,xname,mname,diffdef,stage,doccite,cvqual,numconst,logop + +git latexdiff --no-cleanup --flatten --main new.tex --append-textcmd=${TEXTCMDS} --append-safecmd=${SAFECMDS} --add-to-config='ARRENV=itemdecl;codeblock' -o diff.pdf 917cce5e008263a04aa66db6a5bc1e6d45688f30 -- diff --git a/papers/P2988/new-optional.tex b/papers/P2988/new-optional.tex index 1fe8f5ba..1697f8f5 100644 --- a/papers/P2988/new-optional.tex +++ b/papers/P2988/new-optional.tex @@ -17,14 +17,14 @@ \indexheader{optional}% \begin{codeblock} // mostly freestanding -#include // see \ref{compare.syn} +#include // see \tcode{compare.syn} namespace std { - // \ref{optional.optional}, class template \tcode{optional} for object types + // \ref{optional.optional}, class template \tcode{optional} template class optional; // partially freestanding - @\added{// \ref{optional.optionalref}, class template \tcode{optional} for reference types }@ + @\added{// \ref{optional.optional.ref}, partial specialization of \tcode{optional} for lvalue reference types }@ @\added{template }@ @\added{class optional; // partially freestanding}@ @@ -33,7 +33,7 @@ template constexpr auto format_kind> = range_format::disabled; @\added{template}@ - @\added{constexpr bool ranges::enable_borrowed_range> = is_reference_v;}@ + @\added{constexpr bool ranges::enable_borrowed_range> = true;}@ template concept @\defexposconcept{is-derived-from-optional}@ = requires(const T& t) { // \expos @@ -64,12 +64,12 @@ constexpr compare_three_way_result_t operator<=>(const optional&, const optional&); - // \ref{optional.nullops}, comparison with \tcode{nullopt} + // \tcode{optional.nullops}, comparison with \tcode{nullopt} template constexpr bool operator==(const optional&, nullopt_t) noexcept; template constexpr strong_ordering operator<=>(const optional&, nullopt_t) noexcept; - // \ref{optional.comp.with.t}, comparison with \tcode{T} + // \tcode{optional.comp.with.t}, comparison with \tcode{T} template constexpr bool operator==(const optional&, const U&); template constexpr bool operator==(const T&, const optional&); template constexpr bool operator!=(const optional&, const U&); @@ -104,7 +104,7 @@ } \end{codeblock} -\rSec2[optional.optional]{Class template \tcode{optional} \added{for object types}} +\rSec2[optional.optional]{Class template \tcode{optional}} \rSec3[optional.optional.general]{General} @@ -124,7 +124,6 @@ constexpr optional(nullopt_t) noexcept; constexpr optional(const optional&); constexpr optional(optional&&) noexcept(@\seebelow@); - template constexpr explicit optional(in_place_t, Args&&...); template @@ -213,17 +212,18 @@ \begin{removedblock} \tcode{T} shall be a type +\tcode{T} shall be a type other than \cv{} \tcode{in_place_t} or \cv{} \tcode{nullopt_t} -that meets the \oldconcept{Destructible} requirements (cpp17.destructible). +that meets the \oldconcept{Destructible} requirements (\tcode{cpp17.destructible}). + \end{removedblock} \begin{addedblock} \pnum -A type \tcode{X} is a \defnx{valid contained type}{valid contained type!\idxcode{optional}} for \tcode{optional} if \tcode{X} is an lvalue reference type, -or a complete non-array object type, and \tcode{X} is a type other than \tcode{in_place_t} or \tcode{nullopt_t}. - +A type \tcode{X} is a \defnx{valid contained type}{valid contained type!\idxcode{optional}} for \tcode{optional} if \tcode{X} is an lvalue reference type +or a complete non-array object type, and \tcode{remove_cvref_t} is a type other than \tcode{in_place_t} or \tcode{nullopt_t}. \pnum -\tcode{T} shall be an object type that is a valid contained type for \tcode{optional} and meets the \oldconcept{Destructible} requirements (cpp17.destructible). +If a specialization of \tcode{optional} is instantiated with a type \tcode{T} that is not a valid contained type for \tcode{optional}, the program is ill-formed. If \tcode{T} is an object type, \tcode{T} shall meet the \oldconcept{Destructible} requirements (\tcode{cpp17.destructible}). \end{addedblock} \rSec3[optional.ctor]{Constructors} @@ -953,7 +953,7 @@ \begin{itemdescr} \pnum \constraints -\tcode{F} models \tcode{\libconcept{invocable}<>} and +\tcode{F} models \tcode{\libconcept{invocable}} and \tcode{T} models \libconcept{copy_constructible}. \pnum @@ -980,7 +980,7 @@ \begin{itemdescr} \pnum \constraints -\tcode{F} models \tcode{\libconcept{invocable}<>} and +\tcode{F} models \tcode{\libconcept{invocable}} and \tcode{T} models \libconcept{move_constructible}. \pnum @@ -1002,91 +1002,82 @@ \rSec3[optional.mod]{Modifiers} \begin{addedblock} -\rSec2[optional.optionalref]{Partial specialization of \tcode{optional} for reference types} +\rSec2[optional.optional.ref]{Partial specialization of \tcode{optional} for reference types} -\rSec3[optional.optionalref.general]{General} -[NOTE TO EDITOR: If P3471 is applied the Preconditions become Hardened Preconditions throughout as in P3471] +\rSec3[optional.optional.ref.general]{General} \begin{codeblock} namespace std { template class optional { public: using value_type = T; - using iterator = @\impdefnc@; // see~\ref{optionalref.iterators} + using iterator = @\impdefnc@; // see~\ref{optional.ref.iterators} public: - // \ref{optionalref.ctor}, constructors - constexpr optional() noexcept; - constexpr optional(nullopt_t) noexcept; + // \ref{optional.ref.ctor}, constructors + constexpr optional() noexcept = default; + constexpr optional(nullopt_t) noexcept : optional() {} constexpr optional(const optional& rhs) noexcept = default; template - constexpr_func explicit optional(in_place_t, Arg&& arg); - - template constexpr explicit(@\seebelow@) optional(U&& u) noexcept(@\seebelow@); - - template constexpr explicit(@\seebelow@) optional(optional& rhs) noexcept(@\seebelow@) - template constexpr explicit(@\seebelow@) optional(const optional& rhs) noexcept(@\seebelow@) - template constexpr explicit(@\seebelow@) optional(optional&& rhs) noexcept(@\seebelow@) - template constexpr explicit(@\seebelow@) optional(const optional&& rhs) noexcept(@\seebelow@) + constexpr explicit optional(in_place_t, Arg&& arg); + template + constexpr explicit(@\seebelow@) optional(U&& u) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) optional(optional& rhs) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) optional(const optional& rhs) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) optional(optional&& rhs) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) optional(const optional&& rhs) noexcept(@\seebelow@); constexpr ~optional() = default; - // \ref{optionalref.assign}, assignment + // \ref{optional.ref.assign}, assignment constexpr optional& operator=(nullopt_t) noexcept; constexpr optional& operator=(const optional& rhs) noexcept = default; template constexpr T& emplace(U&& u) noexcept(@\seebelow@); - // \ref{optionalref.swap}, swap + // \ref{optional.ref.swap}, swap constexpr void swap(optional& rhs) noexcept; // \ref{optional.iterators}, iterator support constexpr iterator begin() const noexcept; constexpr iterator end() const noexcept; - // \ref{optionalref.observe}, observers + // \ref{optional.ref.observe}, observers constexpr T* operator->() const noexcept; constexpr T& operator*() const noexcept; constexpr explicit operator bool() const noexcept; constexpr bool has_value() const noexcept; constexpr T& value() const; // freestanding-deleted - template constexpr remove_cv_t value_or(U&& u) const; + template> constexpr remove_cv_t value_or(U&& u) const; - // \ref{optionalref.monadic}, monadic operations + // \ref{optional.ref.monadic}, monadic operations template constexpr auto and_then(F&& f) const; - template constexpr auto transform(F&& f) const -> optional>; + template constexpr optional> transform(F&& f) const; template constexpr optional or_else(F&& f) const; - // \ref{optionalref.mod}, modifiers + // \ref{optional.ref.mod}, modifiers constexpr void reset() noexcept; private: - T* @\exposid{val}@; // \expos - }; + T* @\exposid{val}@ = nullptr; // \expos + // \ref{optional.ref.expos}, exposition only helper functions + template + constexpr void @\exposid{convert-ref-init-val}@(U&& u); // \expos + }; } \end{codeblock} \pnum -An object of \tcode{optional} \defnx{contains a value}{contains a value!\idxcode{optional}} if and only if \tcode{\exposid{val} != nullptr} is \tcode{true}. - -\rSec3[optionalref.ctor]{Constructors} - -\begin{itemdecl} -constexpr optional() noexcept; -constexpr optional(nullopt_t) noexcept; -\end{itemdecl} - -\begin{itemdescr} -\pnum -\effects -Equivalent to: -\begin{codeblock} -@\exposid{val} = nullptr; -\end{codeblock} -\end{itemdescr} +An object of \tcode{optional} \defnx{contains a value}{contains a value!\idxcode{optional.ref}} if and only if \tcode{\exposid{val} != nullptr} is \tcode{true}. +When an \tcode{optional} contains a value, the \defnx{contained value}{contained value!\idxcode{optional.ref}} is a reference to \tcode{\exposid{*val}}. +\rSec3[optional.ref.ctor]{Constructors} \begin{itemdecl} template @@ -1103,219 +1094,173 @@ \pnum \effects - Creates a variable \tcode{r} as if by \tcode{T\& r(std::forward(arg));} and then initializes \exposid{val} with \tcode{addressof(r)}. + Equivalent to: \tcode{\exposid{convert-ref-init-val}(std::forward(arg))}. + + \pnum + \ensures + \tcode{*this} contains a value. \end{itemdescr} \begin{itemdecl} template - constexpr explicit(@\seebelow@) optional(U&& u) noexcept(@\seebelow@); +constexpr explicit(!is_convertible_v) +optional(U&& u) noexcept(is_nothrow_constructible_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}, and - \item \tcode{(is_same_v, optional>)} is \tcode{false}. + \item \tcode{is_same_v, optional>} is \tcode{false}, + \item \tcode{is_same_v, in_place_t>} is \tcode{false}, and + \item \tcode{is_constructible_v} is \tcode{true}. \end{itemize} \pnum \effects - Creates a variable \tcode{r} as if by \tcode{T\& r(std::forward(u));} and then initializes \exposid{val} with \tcode{addressof(r)}. + Equivalent to: \tcode{\exposid{convert-ref-init-val}(std::forward(u))}. + + \pnum + \ensures + \tcode{*this} contains a value. \pnum \remarks - The expression inside \keyword{explicit} is equivalent to: - \begin{codeblock} -!is_convertible_v - \end{codeblock} - The expression inside \keyword{noexcept} is equivalent to: - \begin{codeblock} -is_nothrow_constructible_v - \end{codeblock} This constructor is defined as deleted if - \begin{codeblock} +\begin{codeblock} reference_constructs_from_temporary_v - \end{codeblock} - is \tcode{true} +\end{codeblock} + is \tcode{true}. \end{itemdescr} - - \begin{itemdecl} template -constexpr explicit(@\seebelow@) optional(optional& rhs) noexcept(@\seebelow@); +constexpr explicit(!is_convertible_v) +optional(optional& rhs) noexcept(is_nothrow_constructible_v); \end{itemdecl} \begin{itemdescr} \pnum \constraints \begin{itemize} - \item \tcode{is_constructible_v} is \tcode{true}, - \item \tcode{std::is_same_v, optional>} is \tcode{false}, and - \item \tcode{std::is_same_v} is \tcode{false}. + \item \tcode{is_same_v, optional>} is \tcode{false}, + \item \tcode{is_same_v} is \tcode{false}, and + \item \tcode{is_constructible_v} is \tcode{true}. \end{itemize} \pnum \effects Equivalent to: \begin{codeblock} -if (rhs.has_value()) { - @\exposid{val}@ = addressof(static_cast(*rhs)); -} else { - @\exposid{val}@ = nullptr; -} +if (rhs.has_value()) @\exposid{convert-ref-init-val}@(*rhs); \end{codeblock} \pnum \remarks - The expression inside \keyword{explicit} is equivalent to: - \begin{codeblock} -!is_convertible_v - \end{codeblock} - The expression inside \keyword{noexcept} is equivalent to: - \begin{codeblock} -is_nothrow_constructible_v - \end{codeblock} This constructor is defined as deleted if - \begin{codeblock} +\begin{codeblock} reference_constructs_from_temporary_v - \end{codeblock} +\end{codeblock} is \tcode{true}. \end{itemdescr} \begin{itemdecl} template -constexpr explicit(@\seebelow@) optional(const optional& rhs) noexcept(@\seebelow@); +constexpr explicit(!is_convertible_v) +optional(const optional& rhs) noexcept(is_nothrow_constructible_v); \end{itemdecl} \begin{itemdescr} \pnum \constraints \begin{itemize} - \item \tcode{is_constructible_v} is \tcode{true}, - \item \tcode{std::is_same_v, optional>} is \tcode{false}, and - \item \tcode{std::is_same_v} is \tcode{false}. + \item \tcode{is_same_v, optional>} is \tcode{false}, + \item \tcode{is_same_v} is \tcode{false}, and + \item \tcode{is_constructible_v} is \tcode{true}. \end{itemize} \pnum \effects Equivalent to: \begin{codeblock} -if (rhs.has_value()) { - @\exposid{val}@ = addressof(static_cast(*rhs)); -} else { - @\exposid{val}@ = nullptr; -} +if (rhs.has_value()) @\exposid{convert-ref-init-val}@(*rhs); \end{codeblock} \pnum \remarks - The expression inside \keyword{explicit} is equivalent to: - \begin{codeblock} -!is_convertible_v - \end{codeblock} - The expression inside \keyword{noexcept} is equivalent to: - \begin{codeblock} -is_nothrow_constructible_v - \end{codeblock} This constructor is defined as deleted if - \begin{codeblock} +\begin{codeblock} reference_constructs_from_temporary_v - \end{codeblock} - is \tcode{true} +\end{codeblock} + is \tcode{true}. \end{itemdescr} - \begin{itemdecl} template -constexpr explicit(@\seebelow@) optional(optional&& rhs) noexcept(@\seebelow@); +constexpr explicit(!is_convertible_v) +optional(optional&& rhs) noexcept(is_nothrow_constructible_v); \end{itemdecl} \begin{itemdescr} \pnum \constraints \begin{itemize} - \item \tcode{is_constructible_v} is \tcode{true}, - \item \tcode{std::is_same_v, optional>} is \tcode{false}, and - \item \tcode{std::is_same_v} is \tcode{false}. + \item \tcode{is_same_v, optional>} is \tcode{false}, + \item \tcode{is_same_v} is \tcode{false}, and + \item \tcode{is_constructible_v} is \tcode{true}. \end{itemize} \pnum \effects Equivalent to: \begin{codeblock} -if (rhs.has_value()) { - @\exposid{val}@ = addressof(static_cast(*std::move(rhs))); -} else { - @\exposid{val}@ = nullptr; -} +if (rhs.has_value()) @\exposid{convert-ref-init-val}@(*std::move(rhs)); \end{codeblock} \pnum \remarks - The expression inside \keyword{explicit} is equivalent to: - \begin{codeblock} -!is_convertible_v - \end{codeblock} - The expression inside \keyword{noexcept} is equivalent to: - \begin{codeblock} -is_nothrow_constructible_v - \end{codeblock} This constructor is defined as deleted if - \begin{codeblock} +\begin{codeblock} reference_constructs_from_temporary_v - \end{codeblock} - is \tcode{true} +\end{codeblock} + is \tcode{true}. \end{itemdescr} \begin{itemdecl} template -constexpr explicit(@\seebelow@) optional(const optional&& rhs) noexcept(@\seebelow@); +constexpr explicit(!is_convertible_v) +optional(const optional&& rhs) noexcept(is_nothrow_constructible_v); \end{itemdecl} \begin{itemdescr} \pnum \constraints \begin{itemize} - \item \tcode{is_constructible_v} is \tcode{true}, \item \tcode{is_same_v, optional>} is \tcode{false}, and \item \tcode{is_same_v} is \tcode{false}. + \item \tcode{is_constructible_v} is \tcode{true}, \end{itemize} \pnum \effects Equivalent to: \begin{codeblock} -if (rhs.has_value()) { - @\exposid{val}@ = addressof(static_cast(std::move(*rhs))); -} else { - @\exposid{val}@ = nullptr; -} +if (rhs.has_value()) @\exposid{convert-ref-init-val}@(*std::move(rhs)); \end{codeblock} \pnum \remarks - The expression inside \keyword{explicit} is equivalent to: - \begin{codeblock} -!is_convertible_v - \end{codeblock} - The expression inside \keyword{noexcept} is equivalent to: - \begin{codeblock} -is_nothrow_constructible_v - \end{codeblock} This constructor is defined as deleted if - \begin{codeblock} +\begin{codeblock} reference_constructs_from_temporary_v - \end{codeblock} +\end{codeblock} is \tcode{true}. \end{itemdescr} -\rSec3[optionalref.assign]{Assignment} +\rSec3[optional.ref.assign]{Assignment} \begin{itemdecl} constexpr optional& operator=(nullopt_t) noexcept; @@ -1324,10 +1269,11 @@ \begin{itemdescr} \pnum \effects - Equivalent to: - \begin{codeblock} -@\exposid{val}@ = nullptr; - \end{codeblock} + Assigns \tcode{nullptr} to \exposid{val}. + + \pnum + \ensures + \tcode{*this} does not contain a value. \pnum \returns @@ -1337,7 +1283,7 @@ \begin{itemdecl} template -constexpr T& emplace(U&& u) noexcept(@\seebelow@); +constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v); \end{itemdecl} \begin{itemdescr} @@ -1350,18 +1296,13 @@ \pnum \effects - Assigns \tcode{addressof(static_cast(std::forward(u)))} to \tcode{\exposid{val}}. + Equivalent to: \tcode{\exposid{convert-ref-init-val}(std::forward(u))}. \pnum - \remarks - The expression inside \keyword{noexcept} is equivalent to: - \begin{codeblock} -is_nothrow_constructible_v - \end{codeblock} \end{itemdescr} -\rSec3[optionalref.swap]{Swap} +\rSec3[optional.ref.swap]{Swap} \begin{itemdecl} constexpr void swap(optional& rhs) noexcept; @@ -1370,24 +1311,21 @@ \begin{itemdescr} \pnum \effects - Equivalent to: - \begin{codeblock} -std::swap(@\exposid{val}@, rhs.@\exposid{val}@); - \end{codeblock} + Equivalent to: \tcode{swap(\exposid{val}, rhs.\exposid{val})}. \end{itemdescr} -\rSec3[optionalref.iterators]{Iterator support} +\rSec3[optional.ref.iterators]{Iterator support} \begin{itemdecl} using iterator = @\impdef@; \end{itemdecl} \begin{itemdescr} \pnum -These types -model \libconcept{contiguous_iterator}\iref{iterator.concept.contiguous}, -meet the \oldconcept{RandomAccessIterator} requirements\iref{random.access.iterators}, and -meet the requirements for constexpr iterators\iref{iterator.requirements.general}, +This type +models \libconcept{contiguous_iterator}\iref{iterator.concept.contiguous}, +meets the \oldconcept{RandomAccessIterator} requirements\iref{random.access.iterators}, and +meets the requirements for constexpr iterators\iref{iterator.requirements.general}, with value type \tcode{remove_cv_t}. The reference type is \tcode{T\&} for \tcode{iterator}. @@ -1406,7 +1344,7 @@ \pnum \returns If \tcode{has_value()} is \tcode{true}, - an iterator referring to the referred to value. + an iterator referring to \tcode{*\exposid{val}}. Otherwise, a past-the-end iterator value. \end{itemdescr} @@ -1423,13 +1361,17 @@ -\rSec3[optionalref.observe]{Observers} +\rSec3[optional.ref.observe]{Observers} \begin{itemdecl} constexpr T* operator->() const noexcept; \end{itemdecl} \begin{itemdescr} + \pnum + \hardexpects + \tcode{has_value()} is \tcode{true}. + \pnum \returns \tcode{\exposid{val}}. @@ -1442,8 +1384,8 @@ \begin{itemdescr} \pnum - \expects - \tcode{has_value()} is \tcode{true} + \hardexpects + \tcode{has_value()} is \tcode{true}. \pnum \returns @@ -1458,7 +1400,7 @@ \begin{itemdescr} \pnum \returns - \tcode{\exposid{val} != nullptr} + \tcode{\exposid{val} != nullptr}. \end{itemdescr} \begin{itemdecl} @@ -1468,7 +1410,7 @@ \begin{itemdescr} \pnum \returns - \tcode{\exposid{val} != nullptr} + \tcode{\exposid{val} != nullptr}. \end{itemdescr} \begin{itemdecl} @@ -1480,40 +1422,32 @@ \effects Equivalent to: \begin{codeblock} -if (has_value()) { - return *@\exposid{val}@; -} -throw bad_optional_access(); +return has_value() ? *@\exposid{val}@ : throw bad_optional_access(); \end{codeblock} \end{itemdescr} \begin{itemdecl} -template -constexpr T value_or(U&& u) const; +template> constexpr remove_cv_t value_or(U&& u) const; \end{itemdecl} \begin{itemdescr} + \pnum + Let \tcode{X} be \tcode{remove_cv_t}. + \pnum \mandates - \begin{itemize} - \item \tcode{is_constructible_v, T\&>} is \tcode{true}. - \item \tcode{is_convertible_v>} is \tcode{true}. - \end{itemize} + \tcode{is_constructible_v \&\& is_convertible_v} is \tcode{true}. \pnum \effects Equivalent to: \begin{codeblock} -if (has_value()) { - return remove_cv_t(*@\exposid{val}@); -} else { - return std::forward(u); -} +return has_value() ? *@\exposid{val}@ : static_cast(std::forward(u)); \end{codeblock} \end{itemdescr} -\rSec3[optionalref.monadic]{Monadic operations} +\rSec3[optional.ref.monadic]{Monadic operations} \begin{itemdecl} template @@ -1532,17 +1466,16 @@ \effects Equivalent to: \begin{codeblock} -if (has_value()) { +if (has_value()) return invoke(std::forward(f), *@\exposid{val}@); -} else { +else return remove_cvref_t(); -} \end{codeblock} \end{itemdescr} \begin{itemdecl} template -constexpr auto transform(F&& f) const -> optional>; +constexpr optional>> transform(F&& f) const; \end{itemdecl} \begin{itemdescr} @@ -1551,18 +1484,21 @@ \pnum \mandates - \tcode{U} is a valid contained typename for optional. + The declaration +\begin{codeblock} +U u(invoke(std::forward(f), *@\exposid{val}@)); +\end{codeblock} +is well-formed for some invented variable \tcode{u}. +\begin{note} +There is no requirement that \tcode{U} is movable\iref{dcl.init.general}. +\end{note} \pnum - \effects - Equivalent to: - \begin{codeblock} -if (has_value()) { - return optional{invoke(std::forward(f), *@\exposid{val}@)}; -} else { - return optional{}; -} - \end{codeblock} + \returns + If \tcode{*this} contains a value, an \tcode{optional} object + whose contained value is direct-non-list-initialized with + \tcode{invoke(std::forward(f), *\exposid{val})}; + otherwise, \tcode{optional()}. \end{itemdescr} \begin{itemdecl} @@ -1571,24 +1507,27 @@ \end{itemdecl} \begin{itemdescr} + \pnum + \constraints + \tcode{F} models \tcode{\libconcept{invocable}}. + \pnum \mandates - \tcode{is_same_v, optional>} is \tcode{true}. + \tcode{is_same_v>, optional>} is \tcode{true}. \pnum \effects Equivalent to: \begin{codeblock} - if (has_value()) { - return *@\exposid{val}@; - } else { - return std::forward(f)(); - } +if (has_value()) + return *@\exposid{val}@; +else + return std::forward(f)(); \end{codeblock} \end{itemdescr} -\rSec3[optionalref.mod]{Modifiers} +\rSec3[optional.ref.mod]{Modifiers} \begin{itemdecl} constexpr void reset() noexcept; @@ -1597,11 +1536,23 @@ \begin{itemdescr} \pnum \effects - Equivalent to: - \begin{codeblock} - @\exposid{val}@ = nullptr; - \end{codeblock} - \tcode{\exposid{val}} does not refer to a value. + Assigns \tcode{nullptr} to \exposid{val}. + + \pnum + \ensures + \tcode{*this} does not contain a value. +\end{itemdescr} + +\rSec3[optional.ref.expos]{Exposition only helper functions} +\begin{itemdecl} +template +constexpr void @\exposid{convert-ref-init-val}@(U&& u); // \expos +\end{itemdecl} + +\begin{itemdescr} + \pnum + \effects + Creates a variable \tcode{r} as if by \tcode{T\& r(std::forward(u));} and then initializes \exposid{val} with \tcode{addressof(r)}. \end{itemdescr} \end{addedblock} @@ -1618,9 +1569,16 @@ \begin{itemdescr} \pnum +\begin{removedblock} \constraints \tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_swappable_v} is \tcode{true}. +\end{removedblock} + +\begin{addedblock} +\constraints +\tcode{is_reference_v || (is_move_constructible_v \&\& is_swappable_v)} is \tcode{true}. +\end{addedblock} \pnum \effects @@ -1673,27 +1631,18 @@ \rSec2[optional.hash]{Hash support} -\indexlibrarymember{hash}{optional}% -\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{o} of type \tcode{optional}, -if \tcode{o.has_value() == true}, then \tcode{hash>()(o)} -evaluates to the same value as \tcode{hash>()(*o)}; -otherwise it evaluates to an unspecified value. -The member functions are not guaranteed to be \keyword{noexcept}. -\end{itemdescr} \begin{addedblock} \Sec2[version.syn]{Feature-test macro} -Add the following macro definition to [version.syn], header synopsis, with the value selected by the editor to reflect the date of adoption of this paper: +Change the values of the following macro definition to [version.syn], header synopsis, with the value selected by the editor to reflect the date of adoption of this paper: \begin{codeblock} - #define __cpp_lib_optional_ref 20XXXXL // also in , , + #define __cpp_lib_freestanding_optional 20XXXXL // also in + #define __cpp_lib_optional 20XXXXL // also in \end{codeblock} \end{addedblock} + +%%% Local Variables: +%%% mode: LaTeX +%%% TeX-master: "new" +%%% End: diff --git a/papers/P2988/new.tex b/papers/P2988/new.tex index 1cab0646..fefcf3cd 100644 --- a/papers/P2988/new.tex +++ b/papers/P2988/new.tex @@ -3,8 +3,8 @@ \settocdepth{chapter} \usepackage{minted} \usepackage{fontspec} -\setromanfont{Source Serif Pro} -\setsansfont{Source Sans Pro} +% \setromanfont{Source Serif Pro} +% \setsansfont{Source Sans Pro} % \setmonofont{Source Code Pro} \begin{document} diff --git a/papers/P2988/optional.hpp b/papers/P2988/optional.hpp index af4f4be9..1d6aad4c 100644 --- a/papers/P2988/optional.hpp +++ b/papers/P2988/optional.hpp @@ -11,34 +11,35 @@ class optional { public: using value_type = T; using iterator = - contiguous_iterator; // see [optionalref.iterators] + std::contiguous_iterator; // see [optionalref.iterators] public: // \ref{optionalref.ctor}, constructors - constexpr optional() noexcept; - constexpr optional(nullopt_t) noexcept; + constexpr optional() noexcept = default; + constexpr optional(nullopt_t) noexcept : optional() {} constexpr optional(const optional& rhs) noexcept = default; template - requires(is_constructible_v> && - !reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::reference_constructs_from_temporary_v) constexpr explicit optional(in_place_t, Arg&& arg); template - requires(is_constructible_v && - !(is_same_v, in_place_t>) && - !(is_same_v, optional>) && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) - optional(U&& u) noexcept(is_nothrow_constructible_v) - : value_(addressof(static_cast(std::forward(u)))) {} - - template - requires(is_constructible_v && - !(is_same_v, in_place_t>) && - !(is_same_v, optional>) && - reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !(std::is_same_v, in_place_t>) && + !(std::is_same_v, optional>) && + !std::reference_constructs_from_temporary_v) + constexpr explicit(!std::is_convertible_v) + optional(U&& u) noexcept(std::is_nothrow_constructible_v) { + convert_ref_init_val(u); + } + + template + requires(std::is_constructible_v && + !(std::is_same_v, in_place_t>) && + !(std::is_same_v, optional>) && + std::reference_constructs_from_temporary_v) constexpr optional(U&& u) = delete; // The full set of 4 overloads on optional by value category, doubled to @@ -46,66 +47,66 @@ class optional { // allows correct constraints by propagating the value category from the // optional to the value within the rhs. template - requires(is_constructible_v && - !is_same_v, optional> && - !is_same_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) optional( - optional& rhs) noexcept(is_nothrow_constructible_v); + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + !std::reference_constructs_from_temporary_v) + constexpr explicit(!std::is_convertible_v) optional( + optional& rhs) noexcept(std::is_nothrow_constructible_v); template - requires(is_constructible_v && - !is_same_v, optional> && - !is_same_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + !std::reference_constructs_from_temporary_v) + constexpr explicit(!std::is_convertible_v) optional(const optional& rhs) noexcept( - is_nothrow_constructible_v); + std::is_nothrow_constructible_v); template - requires(is_constructible_v && - !is_same_v, optional> && - !is_same_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + !std::reference_constructs_from_temporary_v) + constexpr explicit(!std::is_convertible_v) optional(optional&& rhs) noexcept( - noexcept(is_nothrow_constructible_v)); + noexcept(std::is_nothrow_constructible_v)); template - requires(is_constructible_v && - !is_same_v, optional> && - !is_same_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + !std::reference_constructs_from_temporary_v) + constexpr explicit(!std::is_convertible_v) optional(const optional&& rhs) noexcept( - noexcept(is_nothrow_constructible_v)); + noexcept(std::is_nothrow_constructible_v)); template - requires(is_constructible_v && - !is_same_v, optional> && - !is_same_v && - reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + std::reference_constructs_from_temporary_v) constexpr optional(optional& rhs) = delete; template - requires(is_constructible_v && - !is_same_v, optional> && - !is_same_v && - reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + std::reference_constructs_from_temporary_v) constexpr optional(const optional& rhs) = delete; template - requires(is_constructible_v && - !is_same_v, optional> && - !is_same_v && - reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + std::reference_constructs_from_temporary_v) constexpr optional(optional&& rhs) = delete; template - requires(is_constructible_v && - !is_same_v, optional> && - !is_same_v && - reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + std::reference_constructs_from_temporary_v) constexpr optional(const optional&& rhs) = delete; // \ref{optionalref.dtor}, destructor @@ -117,9 +118,10 @@ class optional { constexpr optional& operator=(const optional& rhs) noexcept = default; template - requires(is_constructible_v && - !reference_constructs_from_temporary_v); - constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v)) + requires(std::is_constructible_v && + !std::reference_constructs_from_temporary_v) + constexpr T& + emplace(U&& u) noexcept(std::is_nothrow_constructible_v); // \ref{optionalref.swap}, swap constexpr void swap(optional& rhs) noexcept; @@ -134,14 +136,14 @@ class optional { constexpr explicit operator bool() const noexcept; constexpr bool has_value() const noexcept; constexpr T& value() const; - template - constexpr remove_cv_t value_or(U&& u) const; + template > + constexpr std::remove_cv_t value_or(U&& u) const; // \ref{optionalref.monadic}, monadic operations template constexpr auto and_then(F&& f) const; template - constexpr auto transform(F&& f) const -> optional>; + constexpr optional> transform(F&& f) const; template constexpr optional or_else(F&& f) const; @@ -149,88 +151,89 @@ class optional { constexpr void reset() noexcept; private: - T* value_; // exposition only + T* value_ = nullptr; // exposition only + + // \ref{optionalref.expos}, exposition only helper functions + template + constexpr void convert_ref_init_val(U&& u) { + // Creates a variable, \tcode{r}, + // as if by \tcode{T\& r(std::forward(u));} + // and then initializes \exposid{val} with \tcode{addressof(r)} + T& r(std::forward(u)); + value_ = std::addressof(r); + } }; // \rSec3[optionalref.ctor]{Constructors} -template -constexpr optional::optional() noexcept : value_(nullptr) {} - -template -constexpr optional::optional(nullopt_t) noexcept : value_(nullptr) {} - template template - requires(is_constructible_v> && - !reference_constructs_from_temporary_v) -constexpr optional::optional(in_place_t, Arg&& arg) - : value_(addressof(static_cast(std::forward(arg)))) {} + requires(std::is_constructible_v && + !std::reference_constructs_from_temporary_v) +constexpr optional::optional(in_place_t, Arg&& arg) { + convert_ref_init_val(std::forward(arg)); +} // Clang is unhappy with the out-of-line definition // // template // template -// requires(is_constructible_v && !(is_same_v, -// in_place_t>) && +// requires(std::is_constructible_v && +// !(is_same_v, in_place_t>) && // !(is_same_v, optional>) && -// !reference_constructs_from_temporary_v) +// !std::reference_constructs_from_temporary_v) // constexpr optional::optional(U&& u) // noexcept(is_nothrow_constructible_v) -// : value_(addressof(static_cast(forward(u)))) {} +// : value_(std::addressof(static_cast(std::forward(u)))) {} template template - requires(is_constructible_v && - !is_same_v, optional> && !is_same_v && - !reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + !std::reference_constructs_from_temporary_v) constexpr optional::optional(optional& rhs) noexcept( - is_nothrow_constructible_v) { + std::is_nothrow_constructible_v) { if (rhs.has_value()) { - value_ = addressof(static_cast(rhs.value())); - } else { - value_ = nullptr; + convert_ref_init_val(*rhs); } } template template - requires(is_constructible_v && - !is_same_v, optional> && !is_same_v && - !reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + !std::reference_constructs_from_temporary_v) constexpr optional::optional(const optional& rhs) noexcept( - is_nothrow_constructible_v) { + std::is_nothrow_constructible_v) { if (rhs.has_value()) { - value_ = addressof(static_cast(rhs.value())); - } else { - value_ = nullptr; + convert_ref_init_val(*rhs); } } template template - requires(is_constructible_v && - !is_same_v, optional> && !is_same_v && - !reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + !std::reference_constructs_from_temporary_v) constexpr optional::optional(optional&& rhs) noexcept( - noexcept(is_nothrow_constructible_v)) { + noexcept(std::is_nothrow_constructible_v)) { if (rhs.has_value()) { - value_ = addressof(static_cast(std::move(rhs).value())); - } else { - value_ = nullptr; + convert_ref_init_val(*std::move(rhs)); } } template template - requires(is_constructible_v && - !is_same_v, optional> && !is_same_v && - !reference_constructs_from_temporary_v) + requires(std::is_constructible_v && + !std::is_same_v, optional> && + !std::is_same_v && + !std::reference_constructs_from_temporary_v) constexpr optional::optional(const optional&& rhs) noexcept( - noexcept(is_nothrow_constructible_v)) { + noexcept(std::is_nothrow_constructible_v)) { if (rhs.has_value()) { - value_ = addressof(static_cast(std::move(rhs).value())); - } else { - value_ = nullptr; + convert_ref_init_val(*std::move(rhs)); } } @@ -243,11 +246,11 @@ constexpr optional& optional::operator=(nullopt_t) noexcept { template template -constexpr T& optional::emplace(U&& u) noexcept - requires(is_constructible_v && - !reference_constructs_from_temporary_v) -{ - value_ = addressof(static_cast(std::forward(u))); + requires(std::is_constructible_v && + !std::reference_constructs_from_temporary_v) +constexpr T& +optional::emplace(U&& u) noexcept(std::is_nothrow_constructible_v) { + convert_ref_init_val(std::forward(u)); return *value_; } @@ -255,7 +258,7 @@ constexpr T& optional::emplace(U&& u) noexcept template constexpr void optional::swap(optional& rhs) noexcept { - swap(value_, rhs.value_); + std::swap(value_, rhs.value_); } // \rSec3[optionalref.iterators]{Iterator Support} @@ -292,45 +295,47 @@ constexpr bool optional::has_value() const noexcept { template constexpr T& optional::value() const { - if (has_value()) - return *value_; - throw bad_optional_access(); + return has_value() ? *value_ : throw bad_optional_access(); } template template -constexpr remove_cv_t optional::value_or(U&& u) const { - static_assert(is_constructible_v, T&>, +constexpr std::remove_cv_t optional::value_or(U&& u) const { + static_assert(std::is_constructible_v, T&>, "T must be constructible from a T&"); - static_assert(is_convertible_v>, + static_assert(std::is_convertible_v>, "Must be able to convert u to T"); - if (has_value()) { - return remove_cv_t(*value_); - } else { - return std::forward(u); - } + return has_value() ? *value_ + : static_cast>(std::forward(u)); } // \rSec3[optionalref.monadic]{Monadic operations} template template constexpr auto optional::and_then(F&& f) const { - using U = invoke_result_t; - static_assert(is_optional, "F must return an optional"); + using U = std::invoke_result_t; + static_assert(detail::is_optional, "F must return an optional"); if (has_value()) { - return invoke(std::forward(f), *value_); + return std::invoke(std::forward(f), *value_); } else { - return remove_cvref_t(); + return std::remove_cvref_t(); } } template template -constexpr auto -optional::transform(F&& f) const -> optional> { - using U = invoke_result_t; +constexpr optional> +optional::transform(F&& f) const { + using U = std::invoke_result_t; + static_assert(!std::is_same_v, in_place_t>, + "Result must not be in_place_t"); + static_assert(!std::is_same_v, nullopt_t>, + "Result must not be nullopt_t"); + static_assert((std::is_object_v && !std::is_array_v) || + std::is_lvalue_reference_v, + "Result must be an non-array object or an lvalue reference"); if (has_value()) { - return optional{invoke(std::forward(f), *value_)}; + return optional{std::invoke(std::forward(f), *value_)}; } else { return optional{}; } @@ -339,8 +344,9 @@ optional::transform(F&& f) const -> optional> { template template constexpr optional optional::or_else(F&& f) const { - using U = invoke_result_t; - static_assert(is_same_v, optional>); + using U = std::invoke_result_t; + static_assert(std::is_same_v, optional>, + "Result must be an optional"); if (has_value()) { return *value_; } else { @@ -353,3 +359,24 @@ template constexpr void optional::reset() noexcept { value_ = nullptr; } +} // namespace beman::optional + +namespace std { +template + requires requires(T a) { + { + std::hash>{}(a) + } -> std::convertible_to; + } +struct hash> { + static_assert(!is_reference_v, + "hash is not enabled for reference types"); + size_t operator()(const beman::optional::optional& o) const + noexcept(noexcept(hash>{}(*o))) { + if (o) { + return std::hash>{}(*o); + } else { + return 0; + } + } +}; diff --git a/papers/P2988/optional_ref_wording.tex b/papers/P2988/optional_ref_wording.tex index 43d7cac9..44daaa99 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 \#: & P2988R10 \\ + Document \#: & P2988R12 \\ Date: & \today \\ Project: & Programming Language C++ \\ Audience: & LEWG @@ -36,37 +36,13 @@ \chapter*{Changes Since Last Version} \begin{itemize} -\item \textbf{Changes since R8} +\item \textbf{Changes since R10} \begin{itemize} - \item Fix move/assign optional allowing stealing of referenced U + \item Wording changes from LWG review. \end{itemize} -\item \textbf{Changes since R7} +\item \textbf{Changes since R9} \begin{itemize} - \item Wording mandates/constraint fixes - \item Hash on T\& pulled out - \item Notes on wording rendering - \item ``Fix'' make_optional - \end{itemize} -\item \textbf{Changes since R6} - \begin{itemize} - \item strike refref specialization - \item add converting assignment operator - \item add converting in place constructor - \end{itemize} -\item \textbf{Changes since R5} - \begin{itemize} - \item refref specialization - \item fix monadic constraints on base template - \end{itemize} -\item \textbf{Changes since R4} - \begin{itemize} - \item feature test macro - \item value_or updates from P3091 - \end{itemize} -\item \textbf{Changes since R3} - \begin{itemize} - \item make_optional discussion - always value - \item value_or discussion - always value + \item Fix cast in wording removing base/derived UB \end{itemize} \end{itemize} @@ -209,21 +185,12 @@ \section{Relational Operations} The definitions of the relational operators are the same as for the base template. Interoperable comparisons between T and optional work as expected. This is not true for the boost optional. \section{make_optional} -\begin{removedblock} -\sout{Because of existing code, \tcode{make_optional} must return optional rather than optional. Returning optional is consistent and defensible, and a few optional implementations in production make this choice. It is, however, quite easy to construct a make_optional expression that deduces a different category causing possibly dangerous changes to code.} - -\sout{There was some discussion about using library technology to allow selection of the reference overload via the literal spelling \tcode{make_optional}. There was anti-consensus to do so. There are existing instances of that spelling that today return an \tcode{optional}, although it is very likely these are mistakes or possibly other optionals. The spelling \tcode{optional\{\}} is acceptable as there is no multi-argument emplacement version as there is no location to construct such an instance.} -\end{removedblock} - -\begin{addedblock} With further research, the existing uses of make_optional seem to be primarily test cases, and deliberate use seems to be exceedingly rare in the wild. Reflector review was much more positive about removing the misleading ability to create an \tcode{optional} via \tcode{make_optional(x)}. In addition, the multiple argument forms can be used to attempt to construct a optional that contains a reference, but this becomes ill formed because of existing mandates at the type level. In order to preserve existing behavior, where make_optional is not well formed if it constructs a reference, changes to \tcode{make_optional} should be made. Adding a non-type template parameter as the first template parameter to the single argument \tcode{make_optional} and mandating that the multi-argument version not request a reference type as the parameter, will diagnose mistaken use of \tcode{make_optional} and preserve the existing behavior. Since construction of an object in order to make a reference to it to construct an optional containing a reference would always dangle, there do not seem to be any use cases for the multi-argument or initializer list forms of make_optional for reference types, and the constructor form seems to satisfy all cases for single argument construction of a optional containing a reference, there does not seem to be a need for a factory function for optional over reference. -\end{addedblock} - There was also discussion of using \tcode{std::reference_wrapper} to indicate reference use, in analogy with std::tuple. Unfortunately there are existing uses of optional over reference_wrapper as a workaround for lack of reference specialization, and it would be a breaking change for such code. \section{Trivial construction} @@ -242,7 +209,6 @@ \section{Conditional Explicit} As in the base template, \tcode{explicit} is made conditional on the type used to construct the optional. \tcode{explicit(!std::is_convertible_v)}. This is not present in boost::optional, leading to differences in construction between braced initialization and = that can be surprising. \section{value_or} -\removed{Have \tcode{value_or} return a \tcode{T\&}. Check that the supplied value can be bound to a T\&.} After extensive discussion, it seems there is no particularly wonderful solution for \tcode{value_or} that does not involve a time machine. Implementations of optionals that support reference semantics diverge over the return type, and the current one is arguably wrong, and should use something based on \tcode{common_reference_type}, which of course did not exist when \tcode{optional} was standardized. @@ -342,7 +308,7 @@ \section{Deleting dangling overloads} void process(std::optional); void test() { - char const* cstr = “Text”; + char const* cstr = "Text"; std::string s = cstr; process(s); // Picks, optional overload process(cstr); // Ambiguous, but only std::optional is not dangling @@ -389,7 +355,7 @@ \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. +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 seems to be present in many optional implementations that support references. \chapter{Proposal} @@ -412,13 +378,45 @@ \chapter{Impact on the standard} A pure library extension, affecting no other parts of the library or language. -\chapter{Acknowledgements} -Many thanks to all of the reviewers and authors of beman.optional, \cite{The_Beman_Project_beman_optional}, 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{Acknowledgments} +Many thanks to all of the reviewers and authors of beman.optional, \cite{The_Beman_Project_beman_optional}, in particular A. Jiang, Darius Neațu, David Sankel, Eddie Nolan, Jan Kokemüller, Jeff Garland, and River (Xueqing) Wu. Tomasz Kamiński provided extensive support for the library wording of optional. \chapter*{Document history} \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 + \item Hash on T\& pulled out + \item Notes on wording rendering + \item ``Fix'' make_optional + \end{itemize} +\item \textbf{Changes since R6} + \begin{itemize} + \item strike refref specialization + \item add converting assignment operator + \item add converting in place constructor + \end{itemize} +\item \textbf{Changes since R5} + \begin{itemize} + \item refref specialization + \item fix monadic constraints on base template + \end{itemize} +\item \textbf{Changes since R4} + \begin{itemize} + \item feature test macro + \item value_or updates from P3091 + \end{itemize} +\item \textbf{Changes since R3} + \begin{itemize} + \item make_optional discussion - always value + \item value_or discussion - always value + \end{itemize} \item \textbf{Changes since R1} \begin{itemize} \item Design points called out @@ -437,307 +435,5 @@ \chapter*{Document history} \backmatter \chapter*{Implementation} -\begin{minted}{c++} - // ---------------------- - // BASE AND DETAILS ELIDED - // ---------------------- - - /****************/ - /* optional */ - /****************/ - - template - class optional { - public: - using value_type = T&; - using iterator = - detail::contiguous_iterator; // see [optionalref.iterators] - using const_iterator = - detail::contiguous_iterator; // see [optionalref.iterators] - - private: - template - constexpr R make_reference(Arg&& arg) // exposition only - requires is_constructible_v; - - public: - // \ref{optionalref.ctor}, constructors - - constexpr optional() noexcept; - constexpr optional(nullopt_t) noexcept; - constexpr optional(const optional& rhs) noexcept = default; - constexpr optional(optional&& rhs) noexcept = default; - - template - constexpr explicit optional(in_place_t, Arg&& arg) - requires is_constructible_v, Arg>; - - template - requires(!detail::is_optional>) - constexpr explicit(!is_convertible_v) optional(U&& u) noexcept; - template - constexpr explicit(!is_convertible_v) - optional(const optional& rhs) noexcept; - - // \ref{optionalref.dtor}, destructor - constexpr ~optional() = default; - - // \ref{optionalref.assign}, assignment - constexpr optional& operator=(nullopt_t) noexcept; - - constexpr optional& operator=(const optional& rhs) noexcept = default; - constexpr optional& operator=(optional&& rhs) noexcept = default; - - template - requires(!detail::is_optional>) - constexpr optional& operator=(U&& u); - - template - constexpr optional& operator=(const optional& rhs) noexcept; - - template - constexpr optional& operator=(optional&& rhs); - - template - requires(!detail::is_optional>) - constexpr optional& emplace(U&& u) noexcept; - - // \ref{optionalref.swap}, swap - constexpr void swap(optional& rhs) noexcept; - - // \ref{optional.iterators}, iterator support - constexpr iterator begin() noexcept; - constexpr const_iterator begin() const noexcept; - constexpr iterator end() noexcept; - constexpr const_iterator end() const noexcept; - - // \ref{optionalref.observe}, observers - constexpr T* operator->() const noexcept; - constexpr T& operator*() const noexcept; - constexpr explicit operator bool() const noexcept; - constexpr bool has_value() const noexcept; - constexpr T& value() const; - template - constexpr T value_or(U&& u) const; - - // \ref{optionalref.monadic}, monadic operations - template - constexpr auto and_then(F&& f) const; - template - constexpr auto transform(F&& f) const -> optional>; - template - constexpr optional or_else(F&& f) const; - - // \ref{optional.mod}, modifiers - constexpr void reset() noexcept; - - private: - T* value_; // exposition only - }; - - template - template - constexpr R optional::make_reference(Arg&& arg) - requires is_constructible_v - { - static_assert(std::is_reference_v); - #if (__cpp_lib_reference_from_temporary >= 202202L) - static_assert(!std::reference_converts_from_temporary_v, - "Reference conversion from temporary not allowed."); - #endif - R r(std::forward(arg)); - return r; - } - - // \rSec3[optionalref.ctor]{Constructors} - template - constexpr optional::optional() noexcept : value_(nullptr) {} - - template - constexpr optional::optional(nullopt_t) noexcept : value_(nullptr) {} - - template - template - constexpr optional::optional(in_place_t, Arg&& arg) - requires is_constructible_v, Arg> - : value_(addressof( - make_reference>(std::forward(arg)))) { - } - - template - template - requires(!detail::is_optional>) - constexpr optional::optional(U&& u) noexcept : value_(addressof(u)) { - static_assert(is_constructible_v, U>, - "Must be able to bind U to T&"); - static_assert(is_lvalue_reference::value, "U must be an lvalue"); - } - - template - template - constexpr optional::optional(const optional& rhs) noexcept { - static_assert(is_constructible_v, U>, - "Must be able to bind U to T&"); - if (rhs.has_value()) - value_ = to_address(rhs); - else - value_ = nullptr; - } - - // \rSec3[optionalref.assign]{Assignment} - template - constexpr optional& optional::operator=(nullopt_t) noexcept { - value_ = nullptr; - return *this; - } - - template - template - requires(!detail::is_optional>) - constexpr optional& optional::operator=(U&& u) { - static_assert(is_constructible_v, U>, - "Must be able to bind U to T&"); - static_assert(is_lvalue_reference::value, "U must be an lvalue"); - value_ = addressof(u); - return *this; - } - - template - template - constexpr optional& - optional::operator=(const optional& rhs) noexcept { - static_assert(is_constructible_v, U>, - "Must be able to bind U to T&"); - if (rhs.has_value()) - value_ = to_address(rhs); - else - value_ = nullptr; - return *this; - } - - template - template - constexpr optional& optional::operator=(optional&& rhs) { - static_assert(is_constructible_v, U>, - "Must be able to bind U to T&"); - #if (__cpp_lib_reference_from_temporary >= 202202L) - static_assert( - !std::reference_converts_from_temporary_v, - U>, - "Reference conversion from temporary not allowed."); - #endif - if (rhs.has_value()) - value_ = to_address(rhs); - else - value_ = nullptr; - return *this; - } - - template - template - requires(!detail::is_optional>) - constexpr optional& optional::emplace(U&& u) noexcept { - return *this = std::forward(u); - } - - // \rSec3[optionalref.swap]{Swap} - - template - constexpr void optional::swap(optional& rhs) noexcept { - std::swap(value_, rhs.value_); - } - - // \rSec3[optionalref.iterators]{Iterator Support} - template - constexpr optional::iterator optional::begin() noexcept { - return iterator(has_value() ? value_ : nullptr); - }; - - template - constexpr optional::const_iterator optional::begin() const noexcept { - return const_iterator(has_value() ? value_ : nullptr); - }; - - template - constexpr optional::iterator optional::end() noexcept { - return begin() + has_value(); - } - - template - constexpr optional::const_iterator optional::end() const noexcept { - return begin() + has_value(); - } - - // \rSec3[optionalref.observe]{Observers} - template - constexpr T* optional::operator->() const noexcept { - return value_; - } - - template - constexpr T& optional::operator*() const noexcept { - return *value_; - } - - template - constexpr optional::operator bool() const noexcept { - return value_ != nullptr; - } - template - constexpr bool optional::has_value() const noexcept { - return value_ != nullptr; - } - - template - constexpr T& optional::value() const { - if (has_value()) - return *value_; - throw bad_optional_access(); - } - - template - template - constexpr T optional::value_or(U&& u) const { - static_assert(is_copy_constructible_v, "T must be copy constructible"); - static_assert(is_convertible_v, - "Must be able to convert u to T"); - return has_value() ? *value_ : std::forward(u); - } - - // \rSec3[optionalref.monadic]{Monadic operations} - template - template - constexpr auto optional::and_then(F&& f) const { - using U = invoke_result_t; - static_assert(detail::is_optional, "F must return an optional"); - return (has_value()) ? invoke(std::forward(f), *value_) - : remove_cvref_t(); - } - - template - template - constexpr auto optional::transform(F&& f) const - -> optional> { - using U = invoke_result_t; - return (has_value()) ? optional{invoke(std::forward(f), *value_)} - : optional{}; - } - - template - template - constexpr optional optional::or_else(F&& f) const { - using U = invoke_result_t; - static_assert(is_same_v, optional>); - return has_value() ? *value_ : std::forward(f)(); - } - - // \rSec3[optional.mod]{modifiers} - template - constexpr void optional::reset() noexcept { - value_ = nullptr; - } - -\end{minted} +\inputminted{c++}{optional.hpp} \end{document} diff --git a/papers/P2988/stdtex/config.tex b/papers/P2988/stdtex/config.tex index 150ce253..a466361f 100644 --- a/papers/P2988/stdtex/config.tex +++ b/papers/P2988/stdtex/config.tex @@ -6,8 +6,11 @@ % \newcommand{\cppver}{202002L} %% Release date -% \newcommand{\reldate}{\today} +\newcommand{\reldate}{\today} + +%% Core chapters +\newcommand{\lastcorechapter}{cpp} %% Library chapters -% \newcommand{\firstlibchapter}{support} -% \newcommand{\lastlibchapter}{thread} +\newcommand{\firstlibchapter}{support} +\newcommand{\lastlibchapter}{exec} diff --git a/papers/P2988/stdtex/layout.tex b/papers/P2988/stdtex/layout.tex index b74849dd..1f82bd20 100644 --- a/papers/P2988/stdtex/layout.tex +++ b/papers/P2988/stdtex/layout.tex @@ -10,7 +10,7 @@ %%-------------------------------------------------- %% set header and footer positions and sizes -\setheadfoot{\onelineskip}{2\onelineskip} +\setheadfoot{\onelineskip}{4\onelineskip} \setheaderspaces{*}{2\onelineskip}{*} %%-------------------------------------------------- diff --git a/papers/P2988/stdtex/macros.tex b/papers/P2988/stdtex/macros.tex index 837d715c..63f96bd6 100644 --- a/papers/P2988/stdtex/macros.tex +++ b/papers/P2988/stdtex/macros.tex @@ -39,19 +39,19 @@ \newenvironment{addedblock}{\color{addclr}}{\color{black}} \newenvironment{removedblock}{\color{remclr}}{\color{black}} -%%-------------------------------------------------- -%% Grammar extraction. -%%-------------------------------------------------- -\def\gramSec[#1]#2{} +%% %%-------------------------------------------------- +%% %% Grammar extraction. +%% %%-------------------------------------------------- +%% \def\gramSec[#1]#2{} -\makeatletter -\newcommand{\FlushAndPrintGrammar}{% -\immediate\closeout\XTR@out% -\immediate\openout\XTR@out=std-gram-dummy.tmp% -\def\gramSec[##1]##2{\rSec1[##1]{##2}}% -\input{std-gram.ext}% -} -\makeatother +%% \makeatletter +%% \newcommand{\FlushAndPrintGrammar}{% +%% \immediate\closeout\XTR@out% +%% \immediate\openout\XTR@out=std-gram-dummy.tmp% +%% \def\gramSec[##1]##2{\rSec1[##1]{##2}}% +%% \input{std-gram.ext}% +%% } +%% \makeatother %%-------------------------------------------------- % Escaping for index entries. Replaces ! with "! throughout its argument. @@ -64,6 +64,7 @@ %% Cross-references. %%-------------------------------------------------- \newcommand{\addxref}[1]{% + \hypertarget{#1}{}% \glossary[xrefindex]{\indexescape{#1}}{(\ref{\indexescape{#1}})}% } @@ -77,12 +78,12 @@ % Set the xref label for a clause to be "Clause n", not just "n". \makeatletter -\newcommand{\customlabel}[2]{% -\@bsphack \begingroup \protected@edef \@currentlabel {\protect \M@TitleReference{#2}{\M@currentTitle}}\MNR@label{#1}\endgroup \@esphack% +\newcommand{\customlabel}[3]{% +\@bsphack \protected@write\@auxout{}{\string\newlabel{#1}{{#3}{\thepage}{}{#2.\thechapter}{}}} \@esphack% } \makeatother -\newcommand{\clauselabel}[1]{\customlabel{#1}{Clause \thechapter}} -\newcommand{\annexlabel}[1]{\customlabel{#1}{Annex \thechapter}} +\newcommand{\clauselabel}[1]{\customlabel{#1}{chapter}{Clause \thechapter}} +\newcommand{\annexlabel}[1]{\customlabel{#1}{appendix}{Annex \thechapter}} % Use prefix "Annex" in the table of contents \newcommand{\annexnumberlinebox}[2]{Annex #2\space} @@ -92,6 +93,7 @@ % defines a first-level section whose name is "Scope" and whose short % tag is intro.scope. The square brackets are mandatory. \def\Sec#1[#2]#3{% +\addxref{#2}% \ifcase#1\let\s=\chapter\let\l=\clauselabel \or\let\s=\section\let\l=\label \or\let\s=\subsection\let\l=\label @@ -99,7 +101,7 @@ \or\let\s=\paragraph\let\l=\label \or\let\s=\subparagraph\let\l=\label \fi% -\s[#3]{#3\hfill[#2]}\l{#2}\addxref{#2}% +\s[#3]{#3\hfill[#2]}\l{#2}% } % A convenience feature (mostly for the convenience of the Project @@ -118,6 +120,11 @@ \addtocounter{SectionDepth}{\value{SectionDepthBase}} \Sec{\arabic{SectionDepth}}[#2]{#3}} +%%-------------------------------------------------- +% Bibliography +%%-------------------------------------------------- +\newcommand{\supercite}[1]{\textsuperscript{\cite{#1}}} + %%-------------------------------------------------- % Indexing %%-------------------------------------------------- @@ -140,7 +147,7 @@ % \indeximpldef synthesizes a collation key from the argument; that is, an % invocation \indeximpldef{arg} emits an index entry `key@arg`, where `key` -% is derived from `arg` by replacing the folowing list of commands with their +% is derived from `arg` by replacing the following list of commands with their % bare content. This allows, say, collating plain text and code. \newcommand{\indeximpldef}[1]{% \let\otextup\textup% @@ -207,12 +214,21 @@ \newcommand{\libmember}[2]{\indexlibrarymember{#1}{#2}#1} \newcommand{\libspec}[2]{\indexlibrarymemberx{#1}{#2}#1} +% index for macros +% \libmacro is suitable for use directly in header synopses to add the macro +% to both the text and the library index. \libxmacro does the same, but +% prepends a well-rendered double underscore. +\newcommand{\libmacro}[1]{\indexlibraryglobal{#1}\CodeStylex{#1}} +\newcommand{\libxmacro}[1]{\indexlibraryglobal{\idxxname{#1}}\CodeStylex{\xname{#1}}} + % index for library headers -\newcommand{\libheader}[1]{\indexhdr{#1}\tcode{<#1>}} +\newcommand{\libheaderx}[2]{\indexhdr{#1}\tcode{<#2>}} +\newcommand{\libheader}[1]{\libheaderx{#1}{#1}} \newcommand{\indexheader}[1]{\index[headerindex]{\idxhdr{#1}|idxbfpage}} \newcommand{\libheaderdef}[1]{\indexheader{#1}\tcode{<#1>}} \newcommand{\libnoheader}[1]{\indextext{\idxhdr{#1}!absence thereof}\tcode{<#1>}} -\newcommand{\libheaderrefx}[2]{\libheader{#1}\iref{#2}} +\newcommand{\libheaderrefxx}[3]{\libheaderx{#1}{#2}\iref{#3}} +\newcommand{\libheaderrefx}[2]{\libheaderrefxx{#1}{#1}{#2}} \newcommand{\libheaderref}[1]{\libheaderrefx{#1}{#1.syn}} \newcommand{\libdeprheaderref}[1]{\libheaderrefx{#1}{depr.#1.syn}} @@ -277,6 +293,11 @@ \newcommand{\CppXX}{\Cpp{} 2020} \newcommand{\CppXXIII}{\Cpp{} 2023} \newcommand{\CppXXVI}{\Cpp{} 2026} +\newcommand{\IsoCUndated}{ISO/IEC 9899} +\newcommand{\IsoC}{\IsoCUndated{}:2018} +\newcommand{\IsoFloatUndated}{ISO/IEC 60559} +\newcommand{\IsoPosixUndated}{ISO/IEC/IEEE 9945} +\newcommand{\IsoPosix}{\IsoPosixUndated{}:2009} \newcommand{\opt}[1]{#1\ensuremath{_\mathit{\color{black}opt}}} \newcommand{\bigoh}[1]{\ensuremath{\mathscr{O}(#1)}} @@ -309,7 +330,7 @@ \newcommand{\newnoteenvironment}[3]{ \newsubclausecounter{#1} \newenvironment{tail#1} -{\par\small\stepcounter{#1}\noteintro{#2}} +{\par\small\penalty -200\stepcounter{#1}\noteintro{#2}} {\noteoutro{#3}} \newenvironment{#1} {\begin{tail#1}} @@ -345,6 +366,7 @@ \newcommand{\constraints}{\Fundesc{Constraints}} \newcommand{\mandates}{\Fundesc{Mandates}} \newcommand{\expects}{\Fundesc{Preconditions}} +\newcommand{\hardexpects}{\Fundesc{Hardened preconditions}} \newcommand{\effects}{\Fundesc{Effects}} \newcommand{\ensures}{\Fundesc{Postconditions}} \newcommand{\returns}{\Fundesc{Returns}} @@ -355,15 +377,15 @@ \newcommand{\errors}{\Fundesc{Error conditions}} \newcommand{\sync}{\Fundesc{Synchronization}} \newcommand{\implimits}{\Fundesc{Implementation limits}} -\newcommand{\replaceable}{\Fundesc{Replaceable}} \newcommand{\result}{\Fundesc{Result}} \newcommand{\returntype}{\Fundesc{Return type}} \newcommand{\ctype}{\Fundesc{Type}} \newcommand{\templalias}{\Fundesc{Alias template}} %% Cross-reference -\newcommand{\xref}{\textsc{See also:}\space} -\newcommand{\xrefc}[1]{\xref{} ISO C #1} +\newcommand{\xref}[1]{\textsc{See also:}\space #1} +\newcommand{\xrefc}[1]{\xref{\IsoC{}, #1}} +\newcommand{\termref}[3]{\textit{#2}{#3}\iref{#1}} % in Clause 3 %% Inline comma-separated parenthesized references \ExplSyntaxOn @@ -534,6 +556,9 @@ \newcommand{\cv}{\ifmmode\mathit{cv}\else\cvqual{cv}\fi} \newcommand{\numconst}[1]{\textsl{#1}} \newcommand{\logop}[1]{\textsc{#1}} +\DeclareMathOperator{\mullo}{mullo} +\DeclareMathOperator{\mulhi}{mulhi} +\newcommand{\cedef}{\mathrel{\mathop:}=} % "colon-equals definition" %%-------------------------------------------------- %% Environments for code listings. @@ -569,22 +594,6 @@ \lstnewenvironment{codeblock}{\CodeBlockSetup}{} -% Left-align listings titles -\makeatletter -\def\lst@maketitle{\@makeleftcaption\lst@title@dropdelim} -\long\def\@makeleftcaption#1#2{% - \vskip\abovecaptionskip - \sbox\@tempboxa{#1: #2}% - \ifdim \wd\@tempboxa >\hsize - #1: #2\par - \else - \global \@minipagefalse - \hb@xt@\hsize{%\hfil -- REMOVED - \box\@tempboxa\hfil}% - \fi - \vskip\belowcaptionskip}% -\makeatother - \lstnewenvironment{codeblocktu}[1]{% \lstset{title={%\parabullnum{Bullets1}{0pt} #1:}}\CodeBlockSetup}{} @@ -604,6 +613,17 @@ \newcommand{\atsign}{@} \makeatother +%%-------------------------------------------------- +%% Formulae +%%-------------------------------------------------- + +% usage: \begin{formula}{XREF} +\newenvironment{formula}[1] +{\begin{equation}\label{eq:#1}} +{\end{equation}} + +\renewcommand{\eqref}[1]{Formula \nolinebreak[3] \hyperref[eq:#1]{\ref*{eq:#1}}} + %%-------------------------------------------------- %% Indented text %%-------------------------------------------------- @@ -618,9 +638,9 @@ { \lstset{escapechar=@, xleftmargin=0em, - midpenalty=500, + midpenalty=3000, semicolonpenalty=-50, - endpenalty=3000, + endpenalty=2000, aboveskip=2ex, belowskip=0ex % leave this alone: it keeps these things out of the % footnote area @@ -647,7 +667,17 @@ \setlength{\BnfInc}{\BnfIndent} \newlength{\BnfRest} \setlength{\BnfRest}{2\BnfIndent} -\newcommand{\BnfNontermshape}{\small\color{grammar-gray}\sffamily\itshape} +\newcommand{\BnfNontermshape}{% + \small% + \color{grammar-gray}% + % The color setting inserts a \pdfcolorstack entry into the vertical list, + % breaking the connection of the \penalty entry from a preceding heading + % with the \glue entries that precede the grammar snippet. + % Add a penalty here that prevents making those \glue entries page-breaking + % opportunities. + \penalty10000% + \sffamily% + \itshape} \newcommand{\BnfReNontermshape}{\small\rmfamily\itshape} \newcommand{\BnfTermshape}{\small\ttfamily\upshape} diff --git a/papers/P2988/stdtex/styles.tex b/papers/P2988/stdtex/styles.tex index 206f8f1b..46d99d37 100644 --- a/papers/P2988/stdtex/styles.tex +++ b/papers/P2988/stdtex/styles.tex @@ -5,11 +5,11 @@ % footnotes %%-------------------------------------------------- -%% create chapter style +%% create chapter styles \makechapterstyle{cppstd}{% - \renewcommand{\beforechapskip}{\onelineskip} - \renewcommand{\afterchapskip}{\onelineskip} + \setlength{\beforechapskip}{\onelineskip} + \setlength{\afterchapskip}{\onelineskip} \renewcommand{\chapternamenum}{} \renewcommand{\chapnamefont}{\chaptitlefont} \renewcommand{\chapnumfont}{\chaptitlefont} @@ -17,6 +17,16 @@ \renewcommand{\afterchapternum}{} } +\makechapterstyle{cppannex}{% + \setlength{\beforechapskip}{\onelineskip} + \setlength{\afterchapskip}{\onelineskip} + \renewcommand{\chapternamenum}{} + \renewcommand{\chapnamefont}{\chaptitlefont} + \renewcommand{\chapnumfont}{\chaptitlefont} + \renewcommand{\printchapternum}{\chapnumfont\centering\thechapter\protect\\} + \renewcommand{\afterchapternum}{} +} + %%-------------------------------------------------- %% create page styles @@ -86,9 +96,9 @@ %%-------------------------------------------------- % set heading style for annexes -\newcommand{\Annex}[3]{\chapter[#2]{(#3)\protect\\#2\hfill[#1]}\relax\annexlabel{#1}} -\newcommand{\infannex}[2]{\Annex{#1}{#2}{informative}\addxref{#1}} -\newcommand{\normannex}[2]{\Annex{#1}{#2}{normative}\addxref{#1}} +\newcommand{\Annex}[3]{\chapter[#2]{\textnormal{(#3)}\protect\\[3ex]#2\hfill[#1]}\relax\annexlabel{#1}} +\newcommand{\infannex}[2]{\addxref{#1}\Annex{#1}{#2}{informative}} +\newcommand{\normannex}[2]{\addxref{#1}\Annex{#1}{#2}{normative}} %%-------------------------------------------------- %% set footnote style @@ -111,8 +121,10 @@ leftmargin=\bnfindentrest, listparindent=-\bnfindentinc, itemindent=\listparindent} %%-------------------------------------------------- -%% set caption style -\captionstyle{\centering} +%% set caption styles +\DeclareCaptionLabelSeparator{emdash}{ --- } +\captionsetup{justification=centering,labelsep=emdash,font+=bf} +\captionsetup[lstlisting]{justification=raggedright,singlelinecheck=false,font=normal} %%-------------------------------------------------- %% set global styles that get reset by \mainmatter @@ -163,15 +175,17 @@ \else \lst@ifdisplaystyle \lst@EveryDisplay - % make penalty configurable - \par\lst@beginpenalty + \par\lst@beginpenalty % penalty is now configurable \vspace\lst@aboveskip \fi \fi \normalbaselines \abovecaptionskip\lst@abovecaption\relax \belowcaptionskip\lst@belowcaption\relax - \lst@MakeCaption t% + \let\savedallowbreak\allowbreak + \let\allowbreak\relax + \lst@MakeCaption t% % neuter \allowbreak before non-existing top caption + \let\allowbreak\savedallowbreak \lsthk@PreInit \lsthk@Init \lst@ifdisplaystyle \global\let\lst@ltxlabel\@empty diff --git a/papers/P2988/stdtex/tables.tex b/papers/P2988/stdtex/tables.tex index c1f20586..678ec5d9 100644 --- a/papers/P2988/stdtex/tables.tex +++ b/papers/P2988/stdtex/tables.tex @@ -48,6 +48,7 @@ % floattablebase without TableBase, used for lib2dtab2base \newenvironment{floattablebasex}[4] { + \protect\hypertarget{tab:#2}{} \begin{table}[#4] \caption{\label{tab:#2}#1 \quad [tab:#2]} \begin{center} @@ -196,6 +197,7 @@ \newenvironment{LongTable}[3] { \newcommand{\continuedcaption}{\caption[]{#1 (continued)}} + \protect\hypertarget{tab:#2}{} \begin{TableBase} \begin{longtable}{|#3|} \caption{#1 \quad [tab:#2]}\label{tab:#2} @@ -490,7 +492,7 @@ \newcommand{\bottomline}{\rowsep} \newcommand{\hdstyle}[1]{\textbf{##1}} \newcommand{\rowhdr}[1]{\hdstyle{##1}&} - \newcommand{\colhdr}[1]{\multicolumn{1}{|>{\centering}m{#6}|}{\hdstyle{##1}}} + \newcommand{\colhdr}[1]{\multicolumn{1}{>{\centering}m{#6}|}{\hdstyle{##1}}} \begin{floattablebasex} {#1}{#2} {>{\centering}m{#5}|@{}p{0.2\normalbaselineskip}@{}|m{#6}|m{#7} } diff --git a/tests/beman/optional/optional_range_support.t.cpp b/tests/beman/optional/optional_range_support.t.cpp index a368edd0..297ebd6e 100644 --- a/tests/beman/optional/optional_range_support.t.cpp +++ b/tests/beman/optional/optional_range_support.t.cpp @@ -257,7 +257,7 @@ TEST(RangeSupportTest, LoopOverEmptyRange) { TEST(RangeSupportTest, LoopOverNonEmptyRange) { auto lambda = [&] { - const auto expected_value = 0xCAFEBABE; + const int expected_value = 0xCAFEBABE; beman::optional::optional empty{expected_value}; CONSTEXPR_ASSERT_TRUE(empty.has_value()); diff --git a/tests/beman/optional/optional_ref.t.cpp b/tests/beman/optional/optional_ref.t.cpp index ba753470..0bc9deae 100644 --- a/tests/beman/optional/optional_ref.t.cpp +++ b/tests/beman/optional/optional_ref.t.cpp @@ -101,6 +101,17 @@ TEST(OptionalRefTest, BaseDerivedCastConstruction) { EXPECT_TRUE(dopt2.has_value()); } +TEST(OptionalRefTest, BaseDerivedCastEmplace) { + base b; + derived& dref(b); // ok + beman::optional::optional dopt1; + dopt1.emplace(b); + beman::optional::optional dopt2; + dopt2.emplace(dref); + EXPECT_TRUE(dopt1.has_value()); + EXPECT_TRUE(dopt2.has_value()); +} + TEST(OptionalRefTest, Assignment) { beman::optional::optional i1; EXPECT_FALSE(i1); @@ -465,6 +476,11 @@ TEST(OptionalRefTest, Observers) { // auto t4 = so2.value_or("bark"); // std::tuple t("meow"); + auto t5 = so1.value_or({}); + auto t6 = so2.value_or({}); + EXPECT_EQ(t5, std::string()); + EXPECT_EQ(t6, meow); + auto success = std::is_same::value; static_assert(std::is_same::value); EXPECT_TRUE(success); @@ -525,6 +541,26 @@ TEST(OptionalRefTest, Observers) { EXPECT_TRUE(success); } +TEST(OptionalRefTest, AmbiguousConversion) { + struct TypedInt { + int c; + TypedInt(int i) : c(i) {} + operator int() const { return c; } + }; + + TypedInt c{42}; + + auto x1 = beman::optional::optional{}.value_or(c); + auto x2 = beman::optional::optional{}.value_or(7); + + auto x3 = beman::optional::optional{}.value_or(c); + auto x4 = beman::optional::optional{}.value_or(7); + EXPECT_EQ(x1, 42); + EXPECT_EQ(x2, 7); + EXPECT_EQ(x3, 42); + EXPECT_EQ(x4, 7); +} + TEST(OptionalRefTest, MoveCheck) { int x = 0; int& y = std::move(beman::optional::optional(x)).value();