diff --git a/include/beman/optional26/optional.hpp b/include/beman/optional26/optional.hpp index 8b185eef..a4c2c56d 100644 --- a/include/beman/optional26/optional.hpp +++ b/include/beman/optional26/optional.hpp @@ -276,19 +276,29 @@ class optional { template constexpr explicit(!std::is_convertible_v) optional(const optional& rhs) - requires detail::enable_from_other && std::is_convertible_v; + requires(!std::is_reference_v && detail::enable_from_other && + std::is_convertible_v); template constexpr explicit(!std::is_convertible_v) optional(const optional& rhs) - requires detail::enable_from_other && (!std::is_convertible_v); + requires(!std::is_reference_v && detail::enable_from_other && + !std::is_convertible_v); template constexpr explicit(!std::is_convertible_v) optional(optional&& rhs) - requires detail::enable_from_other && std::is_convertible_v; + requires(!std::is_reference_v && detail::enable_from_other && std::is_convertible_v); template constexpr explicit(!std::is_convertible_v) optional(optional&& rhs) - requires detail::enable_from_other && (!std::is_convertible_v); + requires(!std::is_reference_v && detail::enable_from_other && !std::is_convertible_v); + + template + constexpr explicit(!std::is_convertible_v) optional(const optional& rhs) + requires(detail::enable_from_other && std::is_convertible_v); + + template + constexpr explicit(!std::is_convertible_v) optional(const optional& rhs) + requires(detail::enable_from_other && !std::is_convertible_v); // \ref{optional.dtor}, destructor constexpr ~optional() @@ -325,11 +335,15 @@ class optional { template constexpr optional& operator=(const optional& rhs) - requires detail::enable_assign_from_other; + requires(!std::is_reference_v && detail::enable_assign_from_other); template constexpr optional& operator=(optional&& rhs) - requires detail::enable_assign_from_other; + requires(!std::is_reference_v && detail::enable_assign_from_other); + + template + constexpr optional& operator=(const optional& rhs) + requires(detail::enable_assign_from_other); template constexpr T& emplace(Args&&... args); @@ -463,7 +477,8 @@ inline constexpr optional::optional(U&& u) template template inline constexpr optional::optional(const optional& rhs) - requires detail::enable_from_other && std::is_convertible_v + requires(!std::is_reference_v && detail::enable_from_other && + std::is_convertible_v) { if (rhs.has_value()) { construct(*rhs); @@ -473,7 +488,8 @@ inline constexpr optional::optional(const optional& rhs) template template inline constexpr optional::optional(const optional& rhs) - requires detail::enable_from_other && (!std::is_convertible_v) + requires(!std::is_reference_v && detail::enable_from_other && + !std::is_convertible_v) { if (rhs.has_value()) { construct(*rhs); @@ -484,7 +500,7 @@ inline constexpr optional::optional(const optional& rhs) template template inline constexpr optional::optional(optional&& rhs) - requires detail::enable_from_other && std::is_convertible_v + requires(!std::is_reference_v && detail::enable_from_other && std::is_convertible_v) { if (rhs.has_value()) { construct(std::move(*rhs)); @@ -494,13 +510,33 @@ inline constexpr optional::optional(optional&& rhs) template template inline constexpr optional::optional(optional&& rhs) - requires detail::enable_from_other && (!std::is_convertible_v) + requires(!std::is_reference_v && detail::enable_from_other && !std::is_convertible_v) { if (rhs.has_value()) { construct(std::move(*rhs)); } } +template +template +inline constexpr optional::optional(const optional& rhs) + requires(detail::enable_from_other && std::is_convertible_v) +{ + if (rhs.has_value()) { + construct(*rhs); + } +} + +template +template +inline constexpr optional::optional(const optional& rhs) + requires(detail::enable_from_other && !std::is_convertible_v) +{ + if (rhs.has_value()) { + construct(*rhs); + } +} + // 22.5.3.3 Destructor[optional.dtor] template @@ -571,7 +607,7 @@ inline constexpr optional& optional::operator=(U&& u) template template inline constexpr optional& optional::operator=(const optional& rhs) - requires detail::enable_assign_from_other + requires(!std::is_reference_v && detail::enable_assign_from_other) { if (has_value()) { if (rhs.has_value()) { @@ -595,7 +631,7 @@ inline constexpr optional& optional::operator=(const optional& rhs) template template inline constexpr optional& optional::operator=(optional&& rhs) - requires detail::enable_assign_from_other + requires(!std::is_reference_v && detail::enable_assign_from_other) { if (has_value()) { if (rhs.has_value()) { @@ -612,6 +648,26 @@ inline constexpr optional& optional::operator=(optional&& rhs) return *this; } +template +template +inline constexpr optional& optional::operator=(const optional& rhs) + requires(detail::enable_assign_from_other) +{ + if (has_value()) { + if (rhs.has_value()) { + value_ = *rhs; + } else { + hard_reset(); + } + } + + else if (rhs.has_value()) { + construct(*rhs); + } + + return *this; +} + /// Constructs the value in-place, destroying the current one if there is /// one. template diff --git a/src/beman/optional26/tests/optional.t.cpp b/src/beman/optional26/tests/optional.t.cpp index 93a9337f..181d0937 100644 --- a/src/beman/optional26/tests/optional.t.cpp +++ b/src/beman/optional26/tests/optional.t.cpp @@ -914,3 +914,94 @@ TEST(OptionalTest, CanHoldValueOfImmovableType) { beman::optional26::optional o2 = beman::optional26::nullopt; EXPECT_FALSE(o2); } + +// Moving an `optional` should not move the remote value. +TEST(OptionalTest, OptionalFromOptionalRef) { + using beman::optional26::tests::copyable_from_non_const_lvalue_only; + + copyable_from_non_const_lvalue_only cm; + + beman::optional26::optional o1 = cm; + ASSERT_TRUE(o1); + + { + beman::optional26::optional o2 = o1; + ASSERT_TRUE(o2); + } + + beman::optional26::optional o2 = std::move(o1); + ASSERT_TRUE(o2); + + o2 = o1; + ASSERT_TRUE(o2); + + o2 = std::move(o1); + ASSERT_TRUE(o2); + + o2.reset(); + o2 = o1; + ASSERT_TRUE(o2); + + o2.reset(); + o2 = std::move(o1); + ASSERT_TRUE(o2); +} + +TEST(OptionalTest, OptionalFromOptionalRefExplicit) { + using beman::optional26::tests::copyable_from_non_const_lvalue_only; + using beman::optional26::tests::explicitly_convertible_from_non_const_lvalue_only; + + explicitly_convertible_from_non_const_lvalue_only ec; + + beman::optional26::optional o3 = ec; + + beman::optional26::optional o4(o3); + ASSERT_TRUE(o4); + beman::optional26::optional o5(std::move(o3)); + ASSERT_TRUE(o5); +} + +TEST(OptionalTest, OptionalFromOptionalConstRef) { + using beman::optional26::tests::copyable_from_const_lvalue_only; + + copyable_from_const_lvalue_only cm; + + beman::optional26::optional o1 = cm; + ASSERT_TRUE(o1); + + { + beman::optional26::optional o2 = o1; + ASSERT_TRUE(o2); + } + + beman::optional26::optional o2 = std::move(o1); + ASSERT_TRUE(o2); + + o2 = o1; + ASSERT_TRUE(o2); + + o2 = std::move(o1); + ASSERT_TRUE(o2); + + o2.reset(); + o2 = o1; + ASSERT_TRUE(o2); + + o2.reset(); + o2 = std::move(o1); + ASSERT_TRUE(o2); +} + +TEST(OptionalTest, OptionalFromOptionalConstRefExplicit) { + using beman::optional26::tests::copyable_from_const_lvalue_only; + using beman::optional26::tests::explicitly_convertible_from_const_lvalue_only; + + explicitly_convertible_from_const_lvalue_only ec; + + beman::optional26::optional o3 = ec; + + beman::optional26::optional o4(o3); + ASSERT_TRUE(o4); + beman::optional26::optional o5(std::move(o3)); + ASSERT_TRUE(o5); +} diff --git a/src/beman/optional26/tests/test_types.hpp b/src/beman/optional26/tests/test_types.hpp index 6e3e9193..184592dd 100644 --- a/src/beman/optional26/tests/test_types.hpp +++ b/src/beman/optional26/tests/test_types.hpp @@ -71,6 +71,44 @@ struct immovable { immovable& operator=(const immovable&) = delete; }; +struct copyable_from_non_const_lvalue_only { + explicit copyable_from_non_const_lvalue_only() = default; + copyable_from_non_const_lvalue_only(copyable_from_non_const_lvalue_only&) = default; + copyable_from_non_const_lvalue_only(const copyable_from_non_const_lvalue_only&) = delete; + copyable_from_non_const_lvalue_only(copyable_from_non_const_lvalue_only&&) = delete; + copyable_from_non_const_lvalue_only(const copyable_from_non_const_lvalue_only&&) = delete; + copyable_from_non_const_lvalue_only& operator=(copyable_from_non_const_lvalue_only&) = default; + copyable_from_non_const_lvalue_only& operator=(const copyable_from_non_const_lvalue_only&) = delete; + copyable_from_non_const_lvalue_only& operator=(copyable_from_non_const_lvalue_only&&) = delete; + copyable_from_non_const_lvalue_only& operator=(const copyable_from_non_const_lvalue_only&&) = delete; +}; + +struct explicitly_convertible_from_non_const_lvalue_only { + explicit operator copyable_from_non_const_lvalue_only() & { return copyable_from_non_const_lvalue_only{}; } + explicit operator copyable_from_non_const_lvalue_only() const& = delete; + explicit operator copyable_from_non_const_lvalue_only() && = delete; + explicit operator copyable_from_non_const_lvalue_only() const&& = delete; +}; + +struct copyable_from_const_lvalue_only { + explicit copyable_from_const_lvalue_only() = default; + copyable_from_const_lvalue_only(copyable_from_const_lvalue_only&) = delete; + copyable_from_const_lvalue_only(const copyable_from_const_lvalue_only&) = default; + copyable_from_const_lvalue_only(copyable_from_const_lvalue_only&&) = delete; + copyable_from_const_lvalue_only(const copyable_from_const_lvalue_only&&) = delete; + copyable_from_const_lvalue_only& operator=(copyable_from_const_lvalue_only&) = delete; + copyable_from_const_lvalue_only& operator=(const copyable_from_const_lvalue_only&) = default; + copyable_from_const_lvalue_only& operator=(copyable_from_const_lvalue_only&&) = delete; + copyable_from_const_lvalue_only& operator=(const copyable_from_const_lvalue_only&&) = delete; +}; + +struct explicitly_convertible_from_const_lvalue_only { + explicit operator copyable_from_const_lvalue_only() & = delete; + explicit operator copyable_from_const_lvalue_only() const& { return copyable_from_const_lvalue_only{}; } + explicit operator copyable_from_const_lvalue_only() && = delete; + explicit operator copyable_from_const_lvalue_only() const&& = delete; +}; + } // namespace beman::optional26::tests #endif // BEMAN_OPTIONAL26_TESTS_TEST_TYPES_HPP