Skip to content

Commit 139db62

Browse files
authored
Merge pull request #146 from steve-downey/lwg4304
Address LWG4304 value_or ill-formed Make value_or not cause errors when instantiating optional<T&> on non-returnable T.
2 parents 8b2f23f + ed89d27 commit 139db62

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

include/beman/optional/optional.hpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,16 @@ class optional<T&> {
17911791
*/
17921792
constexpr T& value() const;
17931793

1794+
// LWG4304. std::optional<NonReturnable&> is ill-formed due to value_o
1795+
// Resolution:
1796+
// -?- Constraints: T is a non-array object type.
1797+
// -?- Remarks: The return type is unspecified if T is an array type or a
1798+
// non-object type. [Note ?: This is to avoid the declaration being
1799+
// ill-formed. — end note]
1800+
//
1801+
// Implementer Note: Using decay_t as a detail as it is remove_cv_t for
1802+
// non-array objects, and produces a valid type for arrays and functions,
1803+
// which are otherwise `required` out.
17941804
/**
17951805
* @brief Returns the contained value if there is one, otherwise returns `u`.
17961806
*
@@ -1799,7 +1809,8 @@ class optional<T&> {
17991809
* @return std::remove_cv_t<T>
18001810
*/
18011811
template <class U = std::remove_cv_t<T>>
1802-
constexpr std::remove_cv_t<T> value_or(U&& u) const;
1812+
requires(std::is_object_v<T> && !std::is_array_v<T>)
1813+
constexpr std::decay_t<T> value_or(U&& u) const;
18031814

18041815
// \ref{optionalref.monadic}, monadic operations
18051816
/**
@@ -1991,7 +2002,8 @@ constexpr T& optional<T&>::value() const {
19912002

19922003
template <class T>
19932004
template <class U>
1994-
constexpr std::remove_cv_t<T> optional<T&>::value_or(U&& u) const {
2005+
requires(std::is_object_v<T> && !std::is_array_v<T>)
2006+
constexpr std::decay_t<T> optional<T&>::value_or(U&& u) const {
19952007
static_assert(std::is_constructible_v<std::remove_cv_t<T>, T&>, "T must be constructible from a T&");
19962008
static_assert(std::is_convertible_v<U, std::remove_cv_t<T>>, "Must be able to convert u to T");
19972009
return has_value() ? *value_ : static_cast<std::remove_cv_t<T>>(std::forward<U>(u));

tests/beman/optional/optional_ref.test.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,29 @@ TEST(OptionalRefTest, OverloadResolutionChecksDangling) {
864864
// static_assert(std::is_same_v<decltype(check_dangling("abc")), void>);
865865
}
866866

867+
namespace {
868+
int int_func(void) { return 7; }
869+
} // namespace
870+
871+
TEST(OptionalRefTest, NonReturnableRef) {
872+
using IntArray5 = int[5];
873+
beman::optional::optional<IntArray5&> o1;
874+
IntArray5 array;
875+
beman::optional::optional<IntArray5&> o2{array};
876+
EXPECT_FALSE(o1.has_value());
877+
EXPECT_TRUE(o2.has_value());
878+
// value_or removed for array types
879+
// IntArray5 array2;
880+
// auto t1 = o1.value_or(array2);
881+
// auto t2 = o2.value_or(array2);
882+
883+
using IntFunc = int(void);
884+
beman::optional::optional<IntFunc&> o3;
885+
beman::optional::optional<IntFunc&> o4{int_func};
886+
EXPECT_FALSE(o3.has_value());
887+
EXPECT_TRUE(o4.has_value());
888+
}
889+
867890
// beman::optional::optional<int const&> foo() {
868891
// beman::optional::optional<int> o(10);
869892
// return o; // Thanks to a simpler implicit move.

0 commit comments

Comments
 (0)