Skip to content

Commit 069cd74

Browse files
authored
Merge pull request #118 from steve-downey/LWG
Merge results of LWG review
2 parents 960da87 + f74440c commit 069cd74

File tree

19 files changed

+633
-872
lines changed

19 files changed

+633
-872
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ compile_commands.json
2727
.update-submodules
2828
/.install/
2929
/.venv/
30+
/papers/P2988/_minted/

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ include(GNUInstallDirs)
99

1010
# List of all buildable examples.
1111
set(EXAMPLES
12+
base_derived_cast
1213
concept_checks
1314
optional_ref
1415
pythagorean_triples

examples/base_derived_cast.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// examples/optional_ref.cpp -*-C++-*-
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include <beman/optional/optional.hpp>
5+
6+
struct derived;
7+
extern derived d;
8+
struct base {
9+
virtual ~base() = default;
10+
operator derived&() { return d; }
11+
};
12+
13+
struct derived : base {};
14+
15+
derived d;
16+
17+
int example() {
18+
base b;
19+
derived& dref(b); // ok
20+
beman::optional::optional<derived&> dopt(b); // ok
21+
(void)dref;
22+
return 0;
23+
}
24+
25+
int main() { example(); }

examples/optional_ref.cpp

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,36 +44,15 @@ beman::optional::optional<Cat&> api() {
4444

4545
} // namespace std26
4646

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

5252
return 0;
5353
}
5454

55-
struct derived;
56-
extern derived d;
57-
struct base {
58-
virtual ~base() = default;
59-
operator derived&() { return d; }
60-
};
61-
62-
struct derived : base {};
63-
64-
derived d;
65-
66-
int example2() {
67-
base b;
68-
derived& dref(b); // ok
69-
beman::optional::optional<derived&> dopt(b);
70-
return 0;
71-
}
72-
73-
int main() {
74-
example1();
75-
example2();
76-
}
55+
int main() { example(); }
7756
// # build example:
7857
// $ cmake --workflow --preset gcc-14
7958
//

include/beman/optional/optional.hpp

Lines changed: 33 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ inline constexpr bool std::ranges::enable_view<beman::optional::optional<T>> = t
6969

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

7474
// Since P3168R2: Give std::optional Range Support.
7575
#if defined(__cpp_lib_format_ranges)
@@ -699,21 +699,15 @@ inline constexpr bool optional<T>::has_value() const noexcept {
699699
/// bad_optional_access
700700
template <class T>
701701
inline constexpr T& optional<T>::value() & {
702-
if (has_value())
703-
return value_;
704-
throw bad_optional_access();
702+
return has_value() ? value_ : throw bad_optional_access();
705703
}
706704
template <class T>
707705
inline constexpr const T& optional<T>::value() const& {
708-
if (has_value())
709-
return value_;
710-
throw bad_optional_access();
706+
return has_value() ? value_ : throw bad_optional_access();
711707
}
712708
template <class T>
713709
inline constexpr T&& optional<T>::value() && {
714-
if (has_value())
715-
return std::move(value_);
716-
throw bad_optional_access();
710+
return has_value() ? std::move(value_) : throw bad_optional_access();
717711
}
718712

719713
/// Returns the stored value if there is one, otherwise returns `u`
@@ -1090,8 +1084,8 @@ class optional<T&> {
10901084
public:
10911085
// \ref{optionalref.ctor}, constructors
10921086

1093-
constexpr optional() noexcept;
1094-
constexpr optional(nullopt_t) noexcept;
1087+
constexpr optional() noexcept = default;
1088+
constexpr optional(nullopt_t) noexcept : optional() {}
10951089
constexpr optional(const optional& rhs) noexcept = default;
10961090

10971091
template <class Arg>
@@ -1102,12 +1096,9 @@ class optional<T&> {
11021096
requires(std::is_constructible_v<T&, U> && !(std::is_same_v<std::remove_cvref_t<U>, in_place_t>) &&
11031097
!(std::is_same_v<std::remove_cvref_t<U>, optional>) &&
11041098
!detail::reference_constructs_from_temporary_v<T&, U>)
1105-
constexpr explicit(!std::is_convertible_v<U, T&>) optional(U&& u) noexcept(
1106-
std::is_nothrow_constructible_v<T&, U>) { // Creates a variable, \tcode{r}, as if by \tcode{T\&
1107-
// r(std::forward<Arg>(arg));} and then initializes \exposid{val}
1108-
// with \tcode{addressof(r)}
1109-
T& r(std::forward<U>(u));
1110-
value_ = std::addressof(r);
1099+
constexpr explicit(!std::is_convertible_v<U, T&>)
1100+
optional(U&& u) noexcept(std::is_nothrow_constructible_v<T&, U>) {
1101+
convert_ref_init_val(u);
11111102
}
11121103

11131104
template <class U>
@@ -1189,39 +1180,40 @@ class optional<T&> {
11891180
constexpr explicit operator bool() const noexcept;
11901181
constexpr bool has_value() const noexcept;
11911182
constexpr T& value() const;
1192-
template <class U>
1183+
template <class U = std::remove_cv_t<T>>
11931184
constexpr std::remove_cv_t<T> value_or(U&& u) const;
11941185

11951186
// \ref{optionalref.monadic}, monadic operations
11961187
template <class F>
11971188
constexpr auto and_then(F&& f) const;
11981189
template <class F>
1199-
constexpr auto transform(F&& f) const -> optional<std::invoke_result_t<F, T&>>;
1190+
constexpr optional<std::invoke_result_t<F, T&>> transform(F&& f) const;
12001191
template <class F>
12011192
constexpr optional or_else(F&& f) const;
12021193

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

12061197
private:
1207-
T* value_; // exposition only
1198+
T* value_ = nullptr; // exposition only
1199+
1200+
// \ref{optionalref.expos}, exposition only helper functions
1201+
template <class U>
1202+
constexpr void convert_ref_init_val(U&& u) {
1203+
// Creates a variable, \tcode{r},
1204+
// as if by \tcode{T\& r(std::forward<U>(u));}
1205+
// and then initializes \exposid{val} with \tcode{addressof(r)}
1206+
T& r(std::forward<U>(u));
1207+
value_ = std::addressof(r);
1208+
}
12081209
};
12091210

12101211
// \rSec3[optionalref.ctor]{Constructors}
1211-
template <class T>
1212-
constexpr optional<T&>::optional() noexcept : value_(nullptr) {}
1213-
1214-
template <class T>
1215-
constexpr optional<T&>::optional(nullopt_t) noexcept : value_(nullptr) {}
1216-
12171212
template <class T>
12181213
template <class Arg>
12191214
requires(std::is_constructible_v<T&, Arg> && !detail::reference_constructs_from_temporary_v<T&, Arg>)
1220-
constexpr optional<T&>::optional(
1221-
in_place_t, Arg&& arg) { // Creates a variable, \tcode{r}, as if by \tcode{T\& r(std::forward<Arg>(arg));} and
1222-
// then initializes \exposid{val} with \tcode{addressof(r)}
1223-
T& r(std::forward<Arg>(arg));
1224-
value_ = std::addressof(r);
1215+
constexpr optional<T&>::optional(in_place_t, Arg&& arg) {
1216+
convert_ref_init_val(std::forward<Arg>(arg));
12251217
}
12261218

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

@@ -1251,9 +1241,7 @@ template <class U>
12511241
!std::is_same_v<T&, U> && !detail::reference_constructs_from_temporary_v<T&, const U&>)
12521242
constexpr optional<T&>::optional(const optional<U>& rhs) noexcept(std::is_nothrow_constructible_v<T&, const U&>) {
12531243
if (rhs.has_value()) {
1254-
value_ = std::addressof(static_cast<T&>(*rhs));
1255-
} else {
1256-
value_ = nullptr;
1244+
convert_ref_init_val(*rhs);
12571245
}
12581246
}
12591247

@@ -1263,9 +1251,7 @@ template <class U>
12631251
!std::is_same_v<T&, U> && !detail::reference_constructs_from_temporary_v<T&, U>)
12641252
constexpr optional<T&>::optional(optional<U>&& rhs) noexcept(noexcept(std::is_nothrow_constructible_v<T&, U>)) {
12651253
if (rhs.has_value()) {
1266-
value_ = std::addressof(static_cast<T&>(*std::move(rhs)));
1267-
} else {
1268-
value_ = nullptr;
1254+
convert_ref_init_val(*std::move(rhs));
12691255
}
12701256
}
12711257

@@ -1276,9 +1262,7 @@ template <class U>
12761262
constexpr optional<T&>::optional(const optional<U>&& rhs) noexcept(
12771263
noexcept(std::is_nothrow_constructible_v<T&, const U>)) {
12781264
if (rhs.has_value()) {
1279-
value_ = std::addressof(static_cast<T&>(*std::move(rhs)));
1280-
} else {
1281-
value_ = nullptr;
1265+
convert_ref_init_val(*std::move(rhs));
12821266
}
12831267
}
12841268

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

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

13391323
template <class T>
13401324
constexpr T& optional<T&>::value() const {
1341-
if (has_value())
1342-
return *value_;
1343-
throw bad_optional_access();
1325+
return has_value() ? *value_ : throw bad_optional_access();
13441326
}
13451327

13461328
template <class T>
13471329
template <class U>
13481330
constexpr std::remove_cv_t<T> optional<T&>::value_or(U&& u) const {
13491331
static_assert(std::is_constructible_v<std::remove_cv_t<T>, T&>, "T must be constructible from a T&");
13501332
static_assert(std::is_convertible_v<U, std::remove_cv_t<T>>, "Must be able to convert u to T");
1351-
if (has_value()) {
1352-
return std::remove_cv_t<T>(*value_);
1353-
} else {
1354-
return std::forward<U>(u);
1355-
}
1333+
return has_value() ? *value_ : static_cast<std::remove_cv_t<T>>(std::forward<U>(u));
13561334
}
13571335

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

13711349
template <class T>
13721350
template <class F>
1373-
constexpr auto optional<T&>::transform(F&& f) const -> optional<std::invoke_result_t<F, T&>> {
1351+
constexpr optional<std::invoke_result_t<F, T&>> optional<T&>::transform(F&& f) const {
13741352
using U = std::invoke_result_t<F, T&>;
13751353
static_assert(!std::is_same_v<std::remove_cvref_t<U>, in_place_t>, "Result must not be in_place_t");
13761354
static_assert(!std::is_same_v<std::remove_cvref_t<U>, nullopt_t>, "Result must not be nullopt_t");
@@ -1387,7 +1365,7 @@ template <class T>
13871365
template <class F>
13881366
constexpr optional<T&> optional<T&>::or_else(F&& f) const {
13891367
using U = std::invoke_result_t<F>;
1390-
static_assert(std::is_same_v<std::remove_cvref_t<U>, optional>);
1368+
static_assert(std::is_same_v<std::remove_cvref_t<U>, optional>, "Result must be an optional");
13911369
if (has_value()) {
13921370
return *value_;
13931371
} else {

papers/P2988/base-optional.tex

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -934,8 +934,8 @@
934934

935935
\begin{itemdescr}
936936
\pnum
937-
\expects
938-
\tcode{*this} contains a value.
937+
\hardexpects
938+
\tcode{has_value()} is \tcode{true}.
939939

940940
\pnum
941941
\returns
@@ -954,8 +954,8 @@
954954

955955
\begin{itemdescr}
956956
\pnum
957-
\expects
958-
\tcode{*this} contains a value.
957+
\hardexpects
958+
\tcode{has_value()} is \tcode{true}.
959959

960960
\pnum
961961
\returns
@@ -974,8 +974,8 @@
974974

975975
\begin{itemdescr}
976976
\pnum
977-
\expects
978-
\tcode{*this} contains a value.
977+
\hardexpects
978+
\tcode{has_value()} is \tcode{true}.
979979

980980
\pnum
981981
\effects
@@ -1292,7 +1292,7 @@
12921292
class bad_optional_access : public exception {
12931293
public:
12941294
// see \ref{exception} for the specification of the special member functions
1295-
const char* what() const noexcept override;
1295+
constexpr const char* what() const noexcept override;
12961296
};
12971297
}
12981298
\end{codeblock}
@@ -1302,13 +1302,15 @@
13021302

13031303
\indexlibrarymember{what}{bad_optional_access}%
13041304
\begin{itemdecl}
1305-
const char* what() const noexcept override;
1305+
constexpr const char* what() const noexcept override;
13061306
\end{itemdecl}
13071307

13081308
\begin{itemdescr}
13091309
\pnum
13101310
\returns
1311-
An \impldef{return value of \tcode{bad_optional_access::what}} \ntbs{}.
1311+
An \impldef{return value of \tcode{bad_optional_access::what}} \ntbs{},
1312+
which during constant evaluation is encoded with
1313+
the ordinary literal encoding\iref{lex.ccon}.
13121314
\end{itemdescr}
13131315

13141316
\rSec2[optional.relops]{Relational operators}
@@ -1795,3 +1797,8 @@
17951797
otherwise it evaluates to an unspecified value.
17961798
The member functions are not guaranteed to be \keyword{noexcept}.
17971799
\end{itemdescr}
1800+
1801+
%%% Local Variables:
1802+
%%% mode: LaTeX
1803+
%%% TeX-master: "base"
1804+
%%% End:

0 commit comments

Comments
 (0)