Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 24 additions & 18 deletions stl/inc/expected
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public:
}
}

template <class _Uty = _Ty>
template <class _Uty = remove_cv_t<_Ty>>
requires (!is_same_v<remove_cvref_t<_Uty>, in_place_t> && !is_same_v<remove_cvref_t<_Uty>, expected>
&& !_Is_specialization_v<remove_cvref_t<_Uty>, unexpected>
&& (!is_same_v<remove_cv_t<_Ty>, bool>
Expand Down Expand Up @@ -469,7 +469,7 @@ public:
&& _Trivially_move_constructible_assignable_destructible<_Err>
= default;

template <class _Uty = _Ty>
template <class _Uty = remove_cv_t<_Ty>>
requires (!is_same_v<remove_cvref_t<_Uty>, expected> && !_Is_specialization_v<remove_cvref_t<_Uty>, unexpected>
&& is_constructible_v<_Ty, _Uty> && is_assignable_v<_Ty&, _Uty>
&& (is_nothrow_constructible_v<_Ty, _Uty> || is_nothrow_move_constructible_v<_Ty>
Expand Down Expand Up @@ -730,32 +730,38 @@ public:
return _STD move(_Unexpected);
}

template <class _Uty>
_NODISCARD constexpr _Ty value_or(_Uty&& _Other) const& noexcept(
is_nothrow_copy_constructible_v<_Ty> && is_nothrow_convertible_v<_Uty, _Ty>) /* strengthened */ {
static_assert(
is_copy_constructible_v<_Ty>, "is_copy_constructible_v<T> must be true. (N4950 [expected.object.obs]/18)");
static_assert(
is_convertible_v<_Uty, _Ty>, "is_convertible_v<U, T> must be true. (N4950 [expected.object.obs]/18)");
template <class _Uty = remove_cv_t<_Ty>>
_NODISCARD constexpr remove_cv_t<_Ty> value_or(_Uty&& _Other) const& noexcept(
is_nothrow_convertible_v<const _Ty&, remove_cv_t<_Ty>>
&& is_nothrow_convertible_v<_Uty, remove_cv_t<_Ty>>) /* strengthened */ {
static_assert(is_convertible_v<const _Ty&, remove_cv_t<_Ty>>,
"is_convertible_v<const T&, remove_cv_t<T>> must be true. "
"(N5001 [expected.object.obs]/18 as modified by LWG-3424)");
static_assert(is_convertible_v<_Uty, remove_cv_t<_Ty>>,
"is_convertible_v<U, remove_cv_t<T>> must be true. "
"(N5001 [expected.object.obs]/18 as modified by LWG-3424)");

if (_Has_value) {
return _Value;
} else {
return static_cast<_Ty>(_STD forward<_Uty>(_Other));
return static_cast<remove_cv_t<_Ty>>(_STD forward<_Uty>(_Other));
}
}
template <class _Uty>
_NODISCARD constexpr _Ty value_or(_Uty&& _Other) && noexcept(
is_nothrow_move_constructible_v<_Ty> && is_nothrow_convertible_v<_Uty, _Ty>) /* strengthened */ {
static_assert(
is_move_constructible_v<_Ty>, "is_move_constructible_v<T> must be true. (N4950 [expected.object.obs]/20)");
static_assert(
is_convertible_v<_Uty, _Ty>, "is_convertible_v<U, T> must be true. (N4950 [expected.object.obs]/20)");
template <class _Uty = remove_cv_t<_Ty>>
_NODISCARD constexpr remove_cv_t<_Ty> value_or(_Uty&& _Other) && noexcept(
is_nothrow_convertible_v<_Ty, remove_cv_t<_Ty>>
&& is_nothrow_convertible_v<_Uty, remove_cv_t<_Ty>>) /* strengthened */ {
static_assert(is_convertible_v<_Ty, remove_cv_t<_Ty>>,
"is_convertible_v<T, remove_cv_t<T>> must be true. "
"(N5001 [expected.object.obs]/20 as modified by LWG-3424)");
static_assert(is_convertible_v<_Uty, remove_cv_t<_Ty>>,
"is_convertible_v<U, remove_cv_t<T>> must be true. "
"(N5001 [expected.object.obs]/20 as modified by LWG-3424)");

if (_Has_value) {
return _STD move(_Value);
} else {
return static_cast<_Ty>(_STD forward<_Uty>(_Other));
return static_cast<remove_cv_t<_Ty>>(_STD forward<_Uty>(_Other));
}
}

Expand Down
25 changes: 14 additions & 11 deletions stl/inc/optional
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ public:
negation<conjunction<is_same<remove_cv_t<_Ty>, bool>, _Is_specialization<_Remove_cvref_t<_Ty2>, optional>>>,
is_constructible<_Ty, _Ty2>>>;

template <class _Ty2 = _Ty, enable_if_t<_AllowDirectConversion<_Ty2>::value, int> = 0>
template <class _Ty2 = remove_cv_t<_Ty>, enable_if_t<_AllowDirectConversion<_Ty2>::value, int> = 0>
constexpr explicit(!is_convertible_v<_Ty2, _Ty>) optional(_Ty2&& _Right)
noexcept(is_nothrow_constructible_v<_Ty, _Ty2>) // strengthened
: _Mybase(in_place, _STD forward<_Ty2>(_Right)) {}
Expand Down Expand Up @@ -288,10 +288,11 @@ public:
return *this;
}

template <class _Ty2 = _Ty, enable_if_t<conjunction_v<negation<is_same<optional, _Remove_cvref_t<_Ty2>>>,
negation<conjunction<is_scalar<_Ty>, is_same<_Ty, decay_t<_Ty2>>>>,
is_constructible<_Ty, _Ty2>, is_assignable<_Ty&, _Ty2>>,
int> = 0>
template <class _Ty2 = remove_cv_t<_Ty>,
enable_if_t<conjunction_v<negation<is_same<optional, _Remove_cvref_t<_Ty2>>>,
negation<conjunction<is_scalar<_Ty>, is_same<_Ty, decay_t<_Ty2>>>>, is_constructible<_Ty, _Ty2>,
is_assignable<_Ty&, _Ty2>>,
int> = 0>
_CONSTEXPR20 optional& operator=(_Ty2&& _Right)
noexcept(is_nothrow_assignable_v<_Ty&, _Ty2> && is_nothrow_constructible_v<_Ty, _Ty2>) /* strengthened */ {
this->_Assign(_STD forward<_Ty2>(_Right));
Expand Down Expand Up @@ -425,27 +426,29 @@ public:
return _STD move(this->_Value);
}

template <class _Ty2>
template <class _Ty2 = remove_cv_t<_Ty>>
_NODISCARD constexpr remove_cv_t<_Ty> value_or(_Ty2&& _Right) const& {
static_assert(is_convertible_v<const _Ty&, remove_cv_t<_Ty>>,
"The const overload of optional<T>::value_or requires const T& to be convertible to remove_cv_t<T> "
"(N4950 [optional.observe]/15 as modified by LWG-3424).");
static_assert(is_convertible_v<_Ty2, _Ty>,
"optional<T>::value_or(U) requires U to be convertible to T (N4950 [optional.observe]/15).");
static_assert(is_convertible_v<_Ty2, remove_cv_t<_Ty>>,
"optional<T>::value_or(U) requires U to be convertible to remove_cv_t<T> "
"(N4950 [optional.observe]/15 as modified by LWG-3424).");

if (this->_Has_value) {
return static_cast<const _Ty&>(this->_Value);
}

return static_cast<remove_cv_t<_Ty>>(_STD forward<_Ty2>(_Right));
}
template <class _Ty2>
template <class _Ty2 = remove_cv_t<_Ty>>
_NODISCARD constexpr remove_cv_t<_Ty> value_or(_Ty2&& _Right) && {
static_assert(is_convertible_v<_Ty, remove_cv_t<_Ty>>,
"The rvalue overload of optional<T>::value_or requires T to be convertible to remove_cv_t<T> "
"(N4950 [optional.observe]/17 as modified by LWG-3424).");
static_assert(is_convertible_v<_Ty2, _Ty>,
"optional<T>::value_or(U) requires U to be convertible to T (N4950 [optional.observe]/17).");
static_assert(is_convertible_v<_Ty2, remove_cv_t<_Ty>>,
"optional<T>::value_or(U) requires U to be convertible to remove_cv_t<T> "
"(N4950 [optional.observe]/17 as modified by LWG-3424).");

if (this->_Has_value) {
return static_cast<_Ty&&>(this->_Value);
Expand Down
154 changes: 146 additions & 8 deletions tests/std/tests/P0220R1_optional/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8372,6 +8372,11 @@ int run_test()
#include <type_traits>
#include <utility>

#if _HAS_CXX20
#define CONSTEXPR20 constexpr
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
#define CONSTEXPR20 inline
#endif // ^^^ !_HAS_CXX20 ^^^

namespace msvc {
namespace size {
Expand Down Expand Up @@ -8422,12 +8427,6 @@ namespace msvc {
namespace lwg3836 {
static_assert(std::is_convertible_v<std::optional<int>, std::optional<bool>>);
static_assert(std::is_convertible_v<const std::optional<int>&, std::optional<bool>>);

#if _HAS_CXX20
#define CONSTEXPR20 constexpr
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
#define CONSTEXPR20 inline
#endif // ^^^ !_HAS_CXX20 ^^^
CONSTEXPR20 bool run_test() {
std::optional<int> oi = 0;
std::optional<bool> ob = oi;
Expand All @@ -8436,13 +8435,151 @@ namespace msvc {

return true;
}
#undef CONSTEXPR20

#if _HAS_CXX20
static_assert(run_test());
#endif // _HAS_CXX20
} // namespace lwg3836

namespace lwg3886 {
enum class Qualification {
None,
Const,
Volatile,
ConstVolatile,
};

template <class T>
constexpr Qualification CvQualOf =
std::is_const_v<std::remove_reference_t<T>>
? (std::is_volatile_v<std::remove_reference_t<T>> ? Qualification::ConstVolatile : Qualification::Const)
: (std::is_volatile_v<std::remove_reference_t<T>> ? Qualification::Volatile : Qualification::None);

struct QualDistinction {
QualDistinction() = default;

constexpr QualDistinction(QualDistinction&&) noexcept : qual_{Qualification::None} {}
constexpr QualDistinction(const QualDistinction&) noexcept : qual_{Qualification::Const} {}
template <class T,
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
0>
constexpr QualDistinction(T&&) noexcept : qual_{CvQualOf<T>} {}

constexpr QualDistinction& operator=(QualDistinction&&) noexcept {
qual_ = Qualification::None;
return *this;
}
constexpr QualDistinction& operator=(const QualDistinction&) noexcept {
qual_ = Qualification::Const;
return *this;
}
template <class T,
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
0>
constexpr QualDistinction& operator=(T&&) noexcept {
qual_ = CvQualOf<T>;
return *this;
}
template <class T,
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
0>
constexpr const QualDistinction& operator=(T&&) const noexcept {
qual_ = CvQualOf<T>;
return *this;
}
template <class T,
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
0>
volatile QualDistinction& operator=(T&&) volatile noexcept {
qual_ = CvQualOf<T>;
return *this;
}
template <class T,
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
0>
const volatile QualDistinction& operator=(T&&) const volatile noexcept {
qual_ = CvQualOf<T>;
return *this;
}

mutable Qualification qual_ = Qualification::None;
};

constexpr bool test_value_or() {
assert(std::optional<QualDistinction>{}.value_or({}).qual_ == Qualification::None);
assert(std::optional<const QualDistinction>{}.value_or({}).qual_ == Qualification::None);
{
std::optional<QualDistinction> opt;
assert(opt.value_or({}).qual_ == Qualification::None);
}
{
std::optional<const QualDistinction> opt;
assert(opt.value_or({}).qual_ == Qualification::None);
}
return true;
}

CONSTEXPR20 bool test_assignment() {
assert((std::optional<QualDistinction>{} = {QualDistinction{}}).value().qual_ == Qualification::None);
assert((std::optional<const QualDistinction>{} = {QualDistinction{}}).value().qual_ == Qualification::None);
{
std::optional<QualDistinction> opt{std::in_place};
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
opt.reset();
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
}
{
std::optional<const QualDistinction> opt{std::in_place};
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
opt.reset();
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
}
return true;
}

bool test_volatile() {
assert(std::optional<volatile QualDistinction>{}.value_or({}).qual_ == Qualification::None);
assert(std::optional<const volatile QualDistinction>{}.value_or({}).qual_ == Qualification::None);
{
std::optional<volatile QualDistinction> opt;
assert(opt.value_or({}).qual_ == Qualification::None);
}
{
std::optional<const volatile QualDistinction> opt;
assert(opt.value_or({}).qual_ == Qualification::None);
}

assert(
(std::optional<volatile QualDistinction>{} = {QualDistinction{}}).value().qual_ == Qualification::None);
assert((std::optional<const volatile QualDistinction>{} = {QualDistinction{}}).value().qual_
== Qualification::None);
{
std::optional<volatile QualDistinction> opt{std::in_place};
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
opt.reset();
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
}
{
std::optional<const volatile QualDistinction> opt{std::in_place};
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
opt.reset();
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
}

return true;
}

static_assert(test_value_or());
#if _HAS_CXX20
static_assert(test_assignment());
#endif // _HAS_CXX20

void run_test() {
test_value_or();
test_assignment();
test_volatile();
}
} // namespace lwg3886

namespace vso406124 {
// Defend against regression of VSO-406124
void run_test() {
Expand Down Expand Up @@ -8719,6 +8856,7 @@ int main() {
optional_includes_initializer_list::run_test();

msvc::lwg3836::run_test();
msvc::lwg3886::run_test();

msvc::vso406124::run_test();
msvc::vso508126::run_test();
Expand Down
Loading