diff --git a/tests/assignment.cpp b/tests/assignment.cpp index 6e4bd10..5f308e4 100644 --- a/tests/assignment.cpp +++ b/tests/assignment.cpp @@ -1,7 +1,7 @@ #include "catch.hpp" #include "optional.hpp" -TEST_CASE("Assignment", "[assignment]") { +TEST_CASE("Assignment value", "[assignment.value]") { tl::optional o1 = 42; tl::optional o2 = 12; tl::optional o3; @@ -32,3 +32,37 @@ TEST_CASE("Assignment", "[assignment]") { o1 = std::move(o4); REQUIRE(*o1 == 42); } + + +TEST_CASE("Assignment reference", "[assignment.ref]") { + auto i = 42; + auto j = 12; + + tl::optional o1 = i; + tl::optional o2 = j; + tl::optional o3; + + o1 = o1; + REQUIRE(*o1 == 42); + REQUIRE(&*o1 == &i); + + o1 = o2; + REQUIRE(*o1 == 12); + + o1 = o3; + REQUIRE(!o1); + + auto k = 42; + o1 = k; + REQUIRE(*o1 == 42); + REQUIRE(*o1 == i); + REQUIRE(*o1 == k); + REQUIRE(&*o1 != &i); + REQUIRE(&*o1 == &k); + + o1 = tl::nullopt; + REQUIRE(!o1); + + o1 = std::move(o2); + REQUIRE(*o1 == 12); +} diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 7b583f1..a9a9c44 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -2,30 +2,49 @@ #include "optional.hpp" TEST_CASE("Constructors", "[constructors]") { - tl::optional o1; - REQUIRE(!o1); + tl::optional o1; + REQUIRE(!o1); - tl::optional o2 = tl::nullopt; - REQUIRE(!o2); + tl::optional o2 = tl::nullopt; + REQUIRE(!o2); - tl::optional o3 = 42; - REQUIRE(*o3 == 42); + tl::optional o3 = 42; + REQUIRE(*o3 == 42); - tl::optional o4 = o3; - REQUIRE(*o4 == 42); + tl::optional o4 = o3; + REQUIRE(*o4 == 42); - tl::optional o5 = o1; - REQUIRE(!o5); + tl::optional o5 = o1; + REQUIRE(!o5); - tl::optional o6 = std::move(o3); - REQUIRE(*o6 == 42); + tl::optional o6 = std::move(o3); + REQUIRE(*o6 == 42); - tl::optional o7 = 42; - REQUIRE(*o7 == 42); + tl::optional o7 = 42; + REQUIRE(*o7 == 42); - tl::optional o8 = o7; - REQUIRE(*o8 == 42); + tl::optional o8 = o7; + REQUIRE(*o8 == 42); - tl::optional o9 = std::move(o7); - REQUIRE(*o9 == 42); + tl::optional o9 = std::move(o7); + REQUIRE(*o9 == 42); + + { + tl::optional o; + REQUIRE(!o); + + tl::optional oo = o; + REQUIRE(!oo); + } + + { + auto i = 42; + tl::optional o = i; + REQUIRE(o); + REQUIRE(*o == 42); + + tl::optional oo = o; + REQUIRE(oo); + REQUIRE(*oo == 42); + } } diff --git a/tests/make_optional.cpp b/tests/make_optional.cpp index 817e3f2..0376849 100644 --- a/tests/make_optional.cpp +++ b/tests/make_optional.cpp @@ -37,4 +37,8 @@ TEST_CASE("Make optional", "[make_optional]") { REQUIRE(o5->v[1] == 1); REQUIRE(std::get<0>(o5->t) == 2); REQUIRE(std::get<1>(o5->t) == 3); + + auto i = 42; + auto o6 = tl::make_optional(i); + REQUIRE((std::is_same>::value)); } diff --git a/tl/optional.hpp b/tl/optional.hpp index 8e7890b..2b4a21e 100644 --- a/tl/optional.hpp +++ b/tl/optional.hpp @@ -1573,10 +1573,18 @@ void swap(optional &lhs, return lhs.swap(rhs); } -template -inline constexpr optional> make_optional(T &&v) { - return optional>(std::forward(v)); +namespace detail { +struct i_am_secret {}; +} // namespace detail + +template ::value, + detail::decay_t, T>> +inline constexpr optional make_optional(U &&v) { + return optional(std::forward(v)); } + template inline constexpr optional make_optional(Args &&... args) { return optional(in_place, std::forward(args)...); @@ -2056,18 +2064,14 @@ public: /// \synopsis template constexpr optional(U &&u); template < class U = T, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t>> * = nullptr> - constexpr optional(U &&u) : m_value(std::addressof(u)) {} + detail::enable_if_t>::value> * = + nullptr> + constexpr optional(U &&u) : m_value(std::addressof(u)) { + static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); + } /// \exclude - template ::value> * = nullptr> - constexpr optional(U &&u) : base(in_place, std::forward(u)) {} - - /// \exclude - template ::value> * = nullptr> + template constexpr explicit optional(const optional &rhs) : optional(*rhs) {} /// No-op @@ -2091,9 +2095,10 @@ public: /// one. /// \synopsis optional &operator=(U &&u); template >::value> + detail::enable_if_t>::value> * = nullptr> optional &operator=(U &&u) { + static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); m_value = std::addressof(u); return *this; } @@ -2103,10 +2108,9 @@ public: /// Copies the value from `rhs` if there is one. Otherwise resets the stored /// value in `*this`. /// \synopsis optional &operator=(const optional & rhs); - template ::value> * = nullptr> + template optional &operator=(const optional &rhs) { - m_value = std::addressof(rhs.value()); + m_value = std::addressof(rhs.value()); return *this; } @@ -2126,24 +2130,17 @@ public: /// If both have a value, the values are swapped. /// If one has a value, it is moved to the other and the movee is left /// valueless. - void - swap(optional &rhs) noexcept { - std::swap(m_value, rhs.m_value); - } + void swap(optional &rhs) noexcept { std::swap(m_value, rhs.m_value); } /// \returns a pointer to the stored value /// \requires a value is stored /// \group pointer /// \synopsis constexpr const T *operator->() const; - constexpr const T *operator->() const { - return m_value; - } + constexpr const T *operator->() const { return m_value; } /// \group pointer /// \synopsis constexpr T *operator->(); - TL_OPTIONAL_11_CONSTEXPR T *operator->() { - return m_value; - } + TL_OPTIONAL_11_CONSTEXPR T *operator->() { return m_value; } /// \returns the stored value /// \requires a value is stored @@ -2156,9 +2153,7 @@ public: constexpr const T &operator*() const & { return *m_value; } /// \exclude - TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { - return std::move(*m_value); - } + TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { return std::move(*m_value); } #ifndef TL_OPTIONAL_NO_CONSTRR /// \exclude @@ -2193,7 +2188,7 @@ public: /// \exclude TL_OPTIONAL_11_CONSTEXPR T &&value() && { if (has_value()) - return std::move(*m_value); + return std::move(*m_value); throw bad_optional_access(); } @@ -2224,207 +2219,12 @@ public: } /// Destroys the stored value if one exists, making the optional empty - void reset() noexcept { - m_value = nullptr; - } + void reset() noexcept { m_value = nullptr; } + +private: + T *m_value; }; // namespace tl -/// \group relop -/// \brief Compares two optional objects -/// \details If both optionals contain a value, they are compared with `T`s -/// relational operators. Otherwise `lhs` and `rhs` are equal only if they are -/// both empty, and `lhs` is less than `rhs` only if `rhs` is empty and `lhs` -/// is not. -template -inline constexpr bool operator==(const optional &lhs, - const optional &rhs) { - return lhs.has_value() == rhs.has_value() && - (!lhs.has_value() || *lhs == *rhs); -} -/// \group relop -template -inline constexpr bool operator!=(const optional &lhs, - const optional &rhs) { - return lhs.has_value() != rhs.has_value() || - (lhs.has_value() && *lhs != *rhs); -} -/// \group relop -template -inline constexpr bool operator<(const optional &lhs, - const optional &rhs) { - return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); -} -/// \group relop -template -inline constexpr bool operator>(const optional &lhs, - const optional &rhs) { - return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); -} -/// \group relop -template -inline constexpr bool operator<=(const optional &lhs, - const optional &rhs) { - return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); -} -/// \group relop -template -inline constexpr bool operator>=(const optional &lhs, - const optional &rhs) { - return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); -} - -/// \group relop_nullopt -/// \brief Compares an optional to a `nullopt` -/// \details Equivalent to comparing the optional to an empty optional -template -inline constexpr bool operator==(const optional &lhs, nullopt_t) noexcept { - return !lhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator==(nullopt_t, const optional &rhs) noexcept { - return !rhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator!=(const optional &lhs, nullopt_t) noexcept { - return lhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator!=(nullopt_t, const optional &rhs) noexcept { - return rhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator<(const optional &, nullopt_t) noexcept { - return false; -} -/// \group relop_nullopt -template -inline constexpr bool operator<(nullopt_t, const optional &rhs) noexcept { - return rhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator<=(const optional &lhs, nullopt_t) noexcept { - return !lhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator<=(nullopt_t, const optional &) noexcept { - return true; -} -/// \group relop_nullopt -template -inline constexpr bool operator>(const optional &lhs, nullopt_t) noexcept { - return lhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator>(nullopt_t, const optional &) noexcept { - return false; -} -/// \group relop_nullopt -template -inline constexpr bool operator>=(const optional &, nullopt_t) noexcept { - return true; -} -/// \group relop_nullopt -template -inline constexpr bool operator>=(nullopt_t, const optional &rhs) noexcept { - return !rhs.has_value(); -} - -/// \group relop_t -/// \brief Compares the optional with a value. -/// \details If the optional has a value, it is compared with the other value -/// using `T`s relational operators. Otherwise, the optional is considered -/// less than the value. -template -inline constexpr bool operator==(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs == rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator==(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs == *rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator!=(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs != rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator!=(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs != *rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator<(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs < rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator<(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs < *rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator<=(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs <= rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator<=(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs <= *rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator>(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs > rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator>(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs > *rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator>=(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs >= rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator>=(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs >= *rhs : true; -} - -/// \synopsis template \nvoid swap(optional &lhs, optional -/// &rhs); -template ::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> -void swap(optional &lhs, - optional &rhs) noexcept(noexcept(lhs.swap(rhs))) { - return lhs.swap(rhs); -} - -template -inline constexpr optional> make_optional(T &&v) { - return optional>(std::forward(v)); -} -template -inline constexpr optional make_optional(Args &&... args) { - return optional(in_place, std::forward(args)...); -} -template -inline constexpr optional make_optional(std::initializer_list il, - Args &&... args) { - return optional(in_place, il, std::forward(args)...); -} - /// \exclude namespace detail { #ifdef TL_OPTIONAL_CX14