Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
14d400b
Update tex infrastructure from current draft
steve-downey Mar 9, 2025
6ee7d36
Introduce convert-ref-init-val helper function
steve-downey Mar 9, 2025
f3d44e5
Drive by fix for signed/unsigned
steve-downey Mar 9, 2025
fe98463
Fix undefined behavior in emplace and test it
steve-downey Mar 9, 2025
a94c5cf
Move base/derived example out of optional_ref example
steve-downey Mar 9, 2025
917cce5
Update wording as instructed
steve-downey Mar 9, 2025
e85fa11
Fix "This type"
steve-downey Mar 14, 2025
787d618
Use optional.ref. as stable tag base
steve-downey Mar 14, 2025
b4f7b8c
Add checklist from LWG review
steve-downey Mar 14, 2025
b126ae8
Make value and value_or oneliners
steve-downey Mar 15, 2025
f2d7691
Remove stray std:: in wording
steve-downey Mar 15, 2025
51c1d23
value_or should remove_cv_t
steve-downey Mar 15, 2025
68560f3
value_or() to allow using {} as argument
steve-downey Mar 15, 2025
b47faf9
Remove extraneous cast in value_or
steve-downey Mar 15, 2025
c380bad
Add todo checkbox
steve-downey Mar 15, 2025
31b610e
Fix monadic wording
steve-downey Mar 15, 2025
e8186f9
Define "contained value" for optional<T&>
steve-downey Mar 15, 2025
c4a6385
Hash support is untouched
steve-downey Mar 15, 2025
9af9095
Add back some Postcondition blocks
steve-downey Mar 15, 2025
577f16b
Finish checklist
steve-downey Mar 15, 2025
1a0eacc
Fix optional.ref TeX references
steve-downey Mar 15, 2025
87449a4
Fix typos, control characters, acknowledgments
steve-downey Mar 15, 2025
842f1e8
Update changelog and paper number
steve-downey Mar 15, 2025
ce36d3d
Refresh baseline for hardexpects
steve-downey Mar 15, 2025
f5d1b34
Remove editor note, change applied
steve-downey Mar 15, 2025
01a7065
Update std tex macros
steve-downey Mar 17, 2025
ccde69f
Paper after second LWG review
steve-downey Mar 17, 2025
ef2f679
Wording fixes from reflector review
steve-downey Mar 21, 2025
a8822ba
Merge remote-tracking branch 'origin/LWG-20250314' into LWG-20250314
steve-downey Mar 21, 2025
953599c
merge wording
steve-downey Mar 26, 2025
eb637cd
Merge remote-tracking branch 'origin/LWG-20250314' into LWG-20250314
steve-downey Mar 26, 2025
b817696
edits from LWG
steve-downey Mar 26, 2025
a5a1756
Merge remote-tracking branch 'refs/remotes/origin/LWG-20250314' into …
steve-downey Mar 26, 2025
a38cdfb
Reformat example code
steve-downey Mar 26, 2025
2e398cd
Per directions from LWG review
steve-downey Mar 26, 2025
757ae5d
Transform put U back
steve-downey Mar 27, 2025
d36d579
Further copy edit pass
steve-downey Mar 28, 2025
163426c
rewrite borrowed range specialization
steve-downey Mar 28, 2025
83cef25
default optional<T&> constructor
steve-downey Mar 28, 2025
82da4e6
value_or support braced default
steve-downey Mar 28, 2025
d14c5a6
Run latexdiff
steve-downey Mar 28, 2025
288d645
Set master TeX doc for auctex
steve-downey Mar 30, 2025
8404ffc
Use phrasing from `expected` for specialization
steve-downey Mar 30, 2025
fe69592
Put back a tref as tcode
steve-downey Mar 31, 2025
23ce553
Don't try to import the grammar
steve-downey Mar 31, 2025
30db472
Fix typo in error message
steve-downey Mar 31, 2025
be2e3df
Fix ternary op ambiguity
steve-downey Apr 2, 2025
b515c03
Fix some out-of-scope xrefs
steve-downey Apr 2, 2025
b0c298a
Format code nit
steve-downey Apr 2, 2025
0b8603d
Remove braces in wording where not needed
steve-downey Apr 4, 2025
85255a9
Fix angle brackets for concept
steve-downey Apr 4, 2025
7bc1e8c
Apply edits from 20250404 teleconference
steve-downey Apr 4, 2025
a23c2b9
Mark paper with P
steve-downey Apr 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ compile_commands.json
.update-submodules
/.install/
/.venv/
/papers/P2988/_minted/
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ include(GNUInstallDirs)

# List of all buildable examples.
set(EXAMPLES
base_derived_cast
concept_checks
optional_ref
pythagorean_triples
Expand Down
25 changes: 25 additions & 0 deletions examples/base_derived_cast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// examples/optional_ref.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for splitting these examples!


#include <beman/optional/optional.hpp>

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<derived&> dopt(b); // ok
(void)dref;
return 0;
}

int main() { example(); }
25 changes: 2 additions & 23 deletions examples/optional_ref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,36 +44,15 @@ beman::optional::optional<Cat&> api() {

} // namespace std26

int example1() {
int example() {
// Example from P2988R5: optional reference.
[[maybe_unused]] Cat* old_cat = std17::api();
[[maybe_unused]] beman::optional::optional<Cat&> new_cat = std26::api();

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<derived&> dopt(b);
return 0;
}

int main() {
example1();
example2();
}
int main() { example(); }
// # build example:
// $ cmake --workflow --preset gcc-14
//
Expand Down
88 changes: 33 additions & 55 deletions include/beman/optional/optional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ inline constexpr bool std::ranges::enable_view<beman::optional::optional<T>> = t

// Iterators for optional<T&> have life times that are not tied to the optional.
template <class T>
inline constexpr bool std::ranges::enable_borrowed_range<beman::optional::optional<T>> = std::is_reference_v<T>;
inline constexpr bool std::ranges::enable_borrowed_range<beman::optional::optional<T&>> = true;

// Since P3168R2: Give std::optional Range Support.
#if defined(__cpp_lib_format_ranges)
Expand Down Expand Up @@ -699,21 +699,15 @@ inline constexpr bool optional<T>::has_value() const noexcept {
/// bad_optional_access
template <class T>
inline constexpr T& optional<T>::value() & {
if (has_value())
return value_;
throw bad_optional_access();
return has_value() ? value_ : throw bad_optional_access();
}
template <class T>
inline constexpr const T& optional<T>::value() const& {
if (has_value())
return value_;
throw bad_optional_access();
return has_value() ? value_ : throw bad_optional_access();
}
template <class T>
inline constexpr T&& optional<T>::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`
Expand Down Expand Up @@ -1090,8 +1084,8 @@ class optional<T&> {
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 <class Arg>
Expand All @@ -1102,12 +1096,9 @@ class optional<T&> {
requires(std::is_constructible_v<T&, U> && !(std::is_same_v<std::remove_cvref_t<U>, in_place_t>) &&
!(std::is_same_v<std::remove_cvref_t<U>, optional>) &&
!detail::reference_constructs_from_temporary_v<T&, U>)
constexpr explicit(!std::is_convertible_v<U, T&>) optional(U&& u) noexcept(
std::is_nothrow_constructible_v<T&, U>) { // Creates a variable, \tcode{r}, as if by \tcode{T\&
// r(std::forward<Arg>(arg));} and then initializes \exposid{val}
// with \tcode{addressof(r)}
T& r(std::forward<U>(u));
value_ = std::addressof(r);
constexpr explicit(!std::is_convertible_v<U, T&>)
optional(U&& u) noexcept(std::is_nothrow_constructible_v<T&, U>) {
convert_ref_init_val(u);
}

template <class U>
Expand Down Expand Up @@ -1189,39 +1180,40 @@ class optional<T&> {
constexpr explicit operator bool() const noexcept;
constexpr bool has_value() const noexcept;
constexpr T& value() const;
template <class U>
template <class U = std::remove_cv_t<T>>
constexpr std::remove_cv_t<T> value_or(U&& u) const;

// \ref{optionalref.monadic}, monadic operations
template <class F>
constexpr auto and_then(F&& f) const;
template <class F>
constexpr auto transform(F&& f) const -> optional<std::invoke_result_t<F, T&>>;
constexpr optional<std::invoke_result_t<F, T&>> transform(F&& f) const;
template <class F>
constexpr optional or_else(F&& f) const;

// \ref{optional.mod}, modifiers
constexpr void reset() noexcept;

private:
T* value_; // exposition only
T* value_ = nullptr; // exposition only

// \ref{optionalref.expos}, exposition only helper functions
template <class U>
constexpr void convert_ref_init_val(U&& u) {
// Creates a variable, \tcode{r},
// as if by \tcode{T\& r(std::forward<U>(u));}
// and then initializes \exposid{val} with \tcode{addressof(r)}
T& r(std::forward<U>(u));
value_ = std::addressof(r);
}
};

// \rSec3[optionalref.ctor]{Constructors}
template <class T>
constexpr optional<T&>::optional() noexcept : value_(nullptr) {}

template <class T>
constexpr optional<T&>::optional(nullopt_t) noexcept : value_(nullptr) {}

template <class T>
template <class Arg>
requires(std::is_constructible_v<T&, Arg> && !detail::reference_constructs_from_temporary_v<T&, Arg>)
constexpr optional<T&>::optional(
in_place_t, Arg&& arg) { // Creates a variable, \tcode{r}, as if by \tcode{T\& r(std::forward<Arg>(arg));} and
// then initializes \exposid{val} with \tcode{addressof(r)}
T& r(std::forward<Arg>(arg));
value_ = std::addressof(r);
constexpr optional<T&>::optional(in_place_t, Arg&& arg) {
convert_ref_init_val(std::forward<Arg>(arg));
}

// Clang is unhappy with the out-of-line definition
Expand All @@ -1239,9 +1231,7 @@ template <class U>
!std::is_same_v<T&, U> && !detail::reference_constructs_from_temporary_v<T&, U&>)
constexpr optional<T&>::optional(optional<U>& rhs) noexcept(std::is_nothrow_constructible_v<T&, U&>) {
if (rhs.has_value()) {
value_ = std::addressof(static_cast<T&>(*rhs));
} else {
value_ = nullptr;
convert_ref_init_val(*rhs);
}
}

Expand All @@ -1251,9 +1241,7 @@ template <class U>
!std::is_same_v<T&, U> && !detail::reference_constructs_from_temporary_v<T&, const U&>)
constexpr optional<T&>::optional(const optional<U>& rhs) noexcept(std::is_nothrow_constructible_v<T&, const U&>) {
if (rhs.has_value()) {
value_ = std::addressof(static_cast<T&>(*rhs));
} else {
value_ = nullptr;
convert_ref_init_val(*rhs);
}
}

Expand All @@ -1263,9 +1251,7 @@ template <class U>
!std::is_same_v<T&, U> && !detail::reference_constructs_from_temporary_v<T&, U>)
constexpr optional<T&>::optional(optional<U>&& rhs) noexcept(noexcept(std::is_nothrow_constructible_v<T&, U>)) {
if (rhs.has_value()) {
value_ = std::addressof(static_cast<T&>(*std::move(rhs)));
} else {
value_ = nullptr;
convert_ref_init_val(*std::move(rhs));
}
}

Expand All @@ -1276,9 +1262,7 @@ template <class U>
constexpr optional<T&>::optional(const optional<U>&& rhs) noexcept(
noexcept(std::is_nothrow_constructible_v<T&, const U>)) {
if (rhs.has_value()) {
value_ = std::addressof(static_cast<T&>(*std::move(rhs)));
} else {
value_ = nullptr;
convert_ref_init_val(*std::move(rhs));
}
}

Expand All @@ -1293,7 +1277,7 @@ template <class T>
template <class U>
requires(std::is_constructible_v<T&, U> && !detail::reference_constructs_from_temporary_v<T&, U>)
constexpr T& optional<T&>::emplace(U&& u) noexcept(std::is_nothrow_constructible_v<T&, U>) {
value_ = std::addressof(static_cast<T&>(std::forward<U>(u)));
convert_ref_init_val(std::forward<U>(u));
return *value_;
}

Expand Down Expand Up @@ -1338,21 +1322,15 @@ constexpr bool optional<T&>::has_value() const noexcept {

template <class T>
constexpr T& optional<T&>::value() const {
if (has_value())
return *value_;
throw bad_optional_access();
return has_value() ? *value_ : throw bad_optional_access();
}

template <class T>
template <class U>
constexpr std::remove_cv_t<T> optional<T&>::value_or(U&& u) const {
static_assert(std::is_constructible_v<std::remove_cv_t<T>, T&>, "T must be constructible from a T&");
static_assert(std::is_convertible_v<U, std::remove_cv_t<T>>, "Must be able to convert u to T");
if (has_value()) {
return std::remove_cv_t<T>(*value_);
} else {
return std::forward<U>(u);
}
return has_value() ? *value_ : static_cast<std::remove_cv_t<T>>(std::forward<U>(u));
}

// \rSec3[optionalref.monadic]{Monadic operations}
Expand All @@ -1370,7 +1348,7 @@ constexpr auto optional<T&>::and_then(F&& f) const {

template <class T>
template <class F>
constexpr auto optional<T&>::transform(F&& f) const -> optional<std::invoke_result_t<F, T&>> {
constexpr optional<std::invoke_result_t<F, T&>> optional<T&>::transform(F&& f) const {
using U = std::invoke_result_t<F, T&>;
static_assert(!std::is_same_v<std::remove_cvref_t<U>, in_place_t>, "Result must not be in_place_t");
static_assert(!std::is_same_v<std::remove_cvref_t<U>, nullopt_t>, "Result must not be nullopt_t");
Expand All @@ -1387,7 +1365,7 @@ template <class T>
template <class F>
constexpr optional<T&> optional<T&>::or_else(F&& f) const {
using U = std::invoke_result_t<F>;
static_assert(std::is_same_v<std::remove_cvref_t<U>, optional>);
static_assert(std::is_same_v<std::remove_cvref_t<U>, optional>, "Result must be an optional");
if (has_value()) {
return *value_;
} else {
Expand Down
102 changes: 102 additions & 0 deletions lwg-20250314.org
Original file line number Diff line number Diff line change
@@ -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: <a href="https://www.gnu.org/software/emacs/">Emacs</a> 30.1.50 (<a href="https://orgmode.org">Org</a> mode 9.7.11)
#+latex_header:

* Notes
https://wg21.link/P2988r9 std::optional<T&>
SD presenting
[optionalref.iterators]
TS: p1: “These types” -> “This type”
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please format them?

image

L.E. Why do we have duplicated notes?

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<F>
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<F>
- [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<T&>
Loading