From 4d0e92c5f6966e955a7df89764bb75a3c139c828 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Mon, 2 Oct 2017 20:18:40 +0100 Subject: [PATCH] C++11 support --- CMakeLists.txt | 6 +- optional.hpp | 186 +++++++++++++++-------------- tests/monadic.cpp | 290 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 382 insertions(+), 100 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 571da96..b4bf0d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,9 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/relops.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/observers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/monadic.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/tests/constexpr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests/constexpr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/nullopt.cpp) - + add_executable(tests ${TEST_SOURCES}) target_link_libraries(tests Catch) -set_property(TARGET tests PROPERTY CXX_STANDARD 14) +set_property(TARGET tests PROPERTY CXX_STANDARD 11) diff --git a/optional.hpp b/optional.hpp index ae08c6e..45413bc 100644 --- a/optional.hpp +++ b/optional.hpp @@ -17,6 +17,12 @@ #include #include +#if __cplusplus == 201103L +#define TL_OPTIONAL_11_CONSTEXPR +#else +#define TL_OPTIONAL_11_CONSTEXPR constexpr +#endif + namespace tl { template class optional; class monostate {}; @@ -83,7 +89,7 @@ constexpr auto invoke(Fn &&f, Args &&... args) noexcept( template < typename Fn, typename... Args, - std::enable_if_t>{}, int> = 0> + enable_if_t>{}, int> = 0> constexpr auto invoke(Fn &&f, Args &&... args) noexcept( noexcept(std::forward(f)(std::forward(args)...))) -> decltype(std::forward(f)(std::forward(args)...)) { @@ -108,12 +114,11 @@ using invoke_result_t = typename invoke_result::type; template using fixup_void = conditional_t::value, monostate, U>; -// TODO check if we need std::invoke_result here template struct get_invoke_optional_ret { - using type = result_of_t< + using type = invoke_result_t< conditional_t::value, - typename std::remove_reference_t::value_type &, - typename std::remove_reference_t::value_type &&>(U)>; + typename remove_reference_t::value_type &, + typename remove_reference_t::value_type &&>, U>; }; template @@ -498,7 +503,7 @@ public: // [optional.ctor], constructors constexpr optional() noexcept = default; constexpr optional(nullopt_t) noexcept {}; - constexpr optional(const optional &rhs) { + TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) { if (rhs.has_value()) { this->m_has_value = true; new (std::addressof(this->m_value)) T(*rhs); @@ -506,7 +511,7 @@ public: } // TODO conditionally disable - constexpr optional(optional &&rhs) noexcept( + TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) noexcept( std::is_nothrow_move_constructible::value) { if (rhs.has_value()) { this->m_has_value = true; @@ -519,7 +524,7 @@ public: Args &&... args) : base(in_place, std::forward(args)...) {} template - constexpr explicit optional( + TL_OPTIONAL_11_CONSTEXPR explicit optional( detail::enable_if_t &, Args &&...>::value, in_place_t>, @@ -706,26 +711,26 @@ public: constexpr const T *operator->() const { return std::addressof(this->m_value); } - constexpr T *operator->() { return std::addressof(this->m_value); } + TL_OPTIONAL_11_CONSTEXPR T *operator->() { return std::addressof(this->m_value); } constexpr const T &operator*() const & { return this->m_value; } - constexpr T &operator*() & { return this->m_value; } - constexpr T &&operator*() && { return std::move(this->m_value); } + TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; } + TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { return std::move(this->m_value); } constexpr const T &&operator*() const && { return std::move(this->m_value); } constexpr explicit operator bool() const noexcept { return this->m_has_value; } constexpr bool has_value() const noexcept { return this->m_has_value; } - constexpr const T &value() const & { + TL_OPTIONAL_11_CONSTEXPR const T &value() const & { if (has_value()) return this->m_value; throw bad_optional_access(); } - constexpr T &value() & { + TL_OPTIONAL_11_CONSTEXPR T &value() & { if (has_value()) return this->m_value; throw bad_optional_access(); } - constexpr T &&value() && { + TL_OPTIONAL_11_CONSTEXPR T &&value() && { if (has_value()) return std::move(this->m_value); throw bad_optional_access(); @@ -756,25 +761,20 @@ public: } } - template constexpr detail::invoke_result_t bind(F &&f) & { + template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t bind(F &&f) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); - if (!has_value()) - return result(nullopt); - return detail::invoke(std::forward(f), value()); + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - template constexpr detail::invoke_result_t bind(F &&f) && { + template TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t bind(F &&f) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); - if (!has_value()) - return result(nullopt); - - return detail::invoke(std::forward(f), std::move(value())); + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } template @@ -783,10 +783,7 @@ public: static_assert(detail::is_optional::value, "F must return an optional"); - if (!has_value()) - return result(nullopt); - - return detail::invoke(std::forward(f), value()); + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } template @@ -795,14 +792,11 @@ public: static_assert(detail::is_optional::value, "F must return an optional"); - if (!has_value()) - return result(nullopt); - - return detail::invoke(std::forward(f), std::move(value())); + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } template - constexpr detail::invoke_result_t bind(F &&f, E &&e) & { + detail::invoke_result_t bind(F &&f, E &&e) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -810,14 +804,14 @@ public: if (!has_value()) return result(nullopt); - auto ret = detail::invoke(std::forward(f), value()); + auto ret = detail::invoke(std::forward(f), **this); if (!ret) detail::invoke(e); return ret; } template - constexpr detail::invoke_result_t bind(F &&f, E &&e) && { + detail::invoke_result_t bind(F &&f, E &&e) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -825,14 +819,14 @@ public: if (!has_value()) return result(nullopt); - auto ret = detail::invoke(std::forward(f), std::move(value())); + auto ret = detail::invoke(std::forward(f), std::move(**this)); if (!ret) detail::invoke(e); return ret; } template - constexpr detail::invoke_result_t bind(F &&f, E &&e) const & { + detail::invoke_result_t bind(F &&f, E &&e) const & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -840,14 +834,14 @@ public: if (!has_value()) return result(nullopt); - auto ret = detail::invoke(std::forward(f), value()); + auto ret = detail::invoke(std::forward(f), **this); if (!ret) detail::invoke(e); return ret; } template - constexpr detail::invoke_result_t bind(F &&f, E &&e) const && { + detail::invoke_result_t bind(F &&f, E &&e) const && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -855,7 +849,7 @@ public: if (!has_value()) return result(nullopt); - auto ret = detail::invoke(std::forward(f), std::move(value())); + auto ret = detail::invoke(std::forward(f), std::move(**this)); if (!ret) detail::invoke(e); return ret; @@ -863,150 +857,152 @@ public: template * = nullptr, detail::disable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) & { - if (has_value()) - return detail::invoke(std::forward(f), value()); - return nullopt; + detail::get_map_return map(F &&f) & { + using result = detail::get_map_return; + return has_value() ? + detail::invoke(std::forward(f), **this) : + result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) & { + detail::get_map_return map(F &&f) & { if (!has_value()) return nullopt; - detail::invoke(std::forward(f), value()); - return {}; + detail::invoke(std::forward(f), **this); + return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) & { - if (!f.has_value() || !has_value()) - return nullopt; - - return detail::invoke(std::forward(f).value(), value()); + detail::get_map_return map(F &&f) & { + using result = detail::get_map_return; + return (f.has_value() && has_value()) ? + detail::invoke(*std::forward(f), **this) : + result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) & { + detail::get_map_return map(F &&f) & { if (!f.has_value() || !has_value()) return nullopt; - detail::invoke(std::forward(f).value(), value()); - return {}; + detail::invoke(*std::forward(f), **this); + return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) && { - if (has_value()) - return detail::invoke(std::forward(f), std::move(value())); - return {}; + detail::get_map_return map(F &&f) && { + using result = detail::get_map_return; + return has_value() ? + detail::invoke(std::forward(f), std::move(**this)) : + result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) && { + detail::get_map_return map(F &&f) && { if (!has_value()) return nullopt; - detail::invoke(std::forward(f), std::move(value())); - return {}; + detail::invoke(std::forward(f), std::move(**this)); + return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) && { - if (!f.has_value() || !has_value()) - return nullopt; - - return detail::invoke(std::forward(f).value(), std::move(value())); + detail::get_map_return map(F &&f) && { + using result = detail::get_map_return; + return (f.has_value() && has_value()) ? + detail::invoke(*std::forward(f), std::move(**this)) : + result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) && { + detail::get_map_return map(F &&f) && { if (!f.has_value() || !has_value()) return nullopt; - detail::invoke(std::forward(f).value(), std::move(value())); - return {}; + detail::invoke(*std::forward(f), std::move(**this)); + return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const & { - if (has_value()) - return detail::invoke(std::forward(f), value()); - return nullopt; + using result = detail::get_map_return; + return this->has_value() ? result(detail::invoke(std::forward(f), **this)) : result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) const & { + detail::get_map_return map(F &&f) const & { if (!has_value()) return nullopt; - detail::invoke(std::forward(f), value()); - return {}; + detail::invoke(std::forward(f), **this); + return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const & { - if (!f.has_value() || !has_value()) - return nullopt; - - return detail::invoke(std::forward(f).value(), value()); + using result = detail::get_map_return; + return (f.has_value() && has_value()) ? + detail::invoke(*std::forward(f), **this) : + result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) const & { + detail::get_map_return map(F &&f) const & { if (!f.has_value() || !has_value()) return nullopt; - detail::invoke(std::forward(f).value(), value()); - return {}; + detail::invoke(*std::forward(f), **this); + return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const && { - if (has_value()) - return detail::invoke(std::forward(f), std::move(value())); - return nullopt; + using result = detail::get_map_return; + return has_value() ? + detail::invoke(std::forward(f), std::move(**this)) : + result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) const && { + detail::get_map_return map(F &&f) const && { if (!has_value()) return nullopt; - detail::invoke(std::forward(f), std::move(value())); - return {}; + detail::invoke(std::forward(f), std::move(**this)); + return monostate{}; } template * = nullptr, detail::disable_if_ret_void * = nullptr> constexpr detail::get_map_return map(F &&f) const && { - if (!f.has_value() || !has_value()) - return nullopt; - - return detail::invoke(std::forward(f).value(), std::move(value())); + using result = detail::get_map_return; + return (f.has_value() && has_value()) ? + detail::invoke(*std::forward(f), std::move(**this)) : + result(nullopt); } template * = nullptr, detail::enable_if_ret_void * = nullptr> - constexpr detail::get_map_return map(F &&f) const && { + detail::get_map_return map(F &&f) const && { if (!f.has_value() || !has_value()) return nullopt; - detail::invoke(std::forward(f).value(), std::move(value())); - return {}; + detail::invoke(*std::forward(f), std::move(**this)); + return monostate{}; } }; diff --git a/tests/monadic.cpp b/tests/monadic.cpp index a6041e4..7936a87 100644 --- a/tests/monadic.cpp +++ b/tests/monadic.cpp @@ -7,6 +7,10 @@ constexpr bool TOKENPASTE2(rqure, __LINE__) = e; \ REQUIRE(e); + constexpr int get_int(int) { return 42; } +constexpr tl::optional get_opt_int(int) { return 42; } + + // What is Clang Format up to?! TEST_CASE("Monadic operations", "[monadic]"){SECTION("map"){// lhs is empty @@ -22,7 +26,7 @@ STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o2r.value() == 42); struct rval_call_map { - auto operator()(int) && { return 42.0; }; + double operator()(int) && { return 42.0; }; }; // ensure that function object is forwarded @@ -56,8 +60,174 @@ auto f7 = tl::make_optional([](const int &i) { return; }); auto o7r = o7.map(f7); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o6r.has_value()); + +// test each overload in turn +tl::optional o8 = 42; +auto o8r = o8.map([](int){return 42;}); +REQUIRE(*o8r == 42); + +tl::optional o9 = 42; +auto o9r = o9.map([](int){return;}); +REQUIRE(o9r); + +tl::optional o10 = 42; +auto o10r = o10.map(tl::make_optional([](int){return 42;})); +REQUIRE(*o10r == 42); + +tl::optional o11 = 42; +auto o11r = o11.map(tl::make_optional([](int){return;})); +REQUIRE(o11r); + +tl::optional o12 = 42; +auto o12r = std::move(o12).map([](int){return 42;}); +REQUIRE(*o12r == 42); + +tl::optional o13 = 42; +auto o13r = std::move(o13).map([](int){return;}); +REQUIRE(o13r); + +tl::optional o14 = 42; +auto o14r = std::move(o14).map(tl::make_optional([](int){return 42;})); +REQUIRE(*o14r == 42); + +tl::optional o15 = 42; +auto o15r = std::move(o15).map(tl::make_optional([](int){return;})); +REQUIRE(o15r); + +const tl::optional o16 = 42; +auto o16r = o16.map([](int){return 42;}); +REQUIRE(*o16r == 42); + +const tl::optional o17 = 42; +auto o17r = o17.map([](int){return;}); +REQUIRE(o17r); + +const tl::optional o18 = 42; +auto o18r = o18.map(tl::make_optional([](int){return 42;})); +REQUIRE(*o18r == 42); + +const tl::optional o19 = 42; +auto o19r = o19.map(tl::make_optional([](int){return;})); +REQUIRE(o19r); + +const tl::optional o20 = 42; +auto o20r = std::move(o20).map([](int){return 42;}); +REQUIRE(*o20r == 42); + +const tl::optional o21 = 42; +auto o21r = std::move(o21).map([](int){return;}); +REQUIRE(o21r); + +const tl::optional o22 = 42; +auto o22r = std::move(o22).map(tl::make_optional([](int){return 42;})); +REQUIRE(*o22r == 42); + +const tl::optional o23 = 42; +auto o23r = std::move(o23).map(tl::make_optional([](int){return;})); +REQUIRE(o23r); + +tl::optional o24 = tl::nullopt; +auto o24r = o24.map([](int){return 42;}); +REQUIRE(!o24r); + +tl::optional o25 = tl::nullopt; +auto o25r = o25.map([](int){return;}); +REQUIRE(!o25r); + +tl::optional o26 = tl::nullopt; +auto o26r = o26.map(tl::make_optional([](int){return 42;})); +REQUIRE(!o26r); + +tl::optional o27 = tl::nullopt; +auto o27r = o27.map(tl::make_optional([](int){return;})); +REQUIRE(!o27r); + +tl::optional o28 = tl::nullopt; +auto o28r = std::move(o28).map([](int){return 42;}); +REQUIRE(!o28r); + +tl::optional o29 = tl::nullopt; +auto o29r = std::move(o29).map([](int){return;}); +REQUIRE(!o29r); + +tl::optional o30 = tl::nullopt; +auto o30r = std::move(o30).map(tl::make_optional([](int){return 42;})); +REQUIRE(!o30r); + +tl::optional o31 = tl::nullopt; +auto o31r = std::move(o31).map(tl::make_optional([](int){return;})); +REQUIRE(!o31r); + +const tl::optional o32 = tl::nullopt; +auto o32r = o32.map([](int){return 42;}); +REQUIRE(!o32r); + +const tl::optional o33 = tl::nullopt; +auto o33r = o33.map([](int){return;}); +REQUIRE(!o33r); + +const tl::optional o34 = tl::nullopt; +auto o34r = o34.map(tl::make_optional([](int){return 42;})); +REQUIRE(!o34r); + +const tl::optional o35 = tl::nullopt; +auto o35r = o35.map(tl::make_optional([](int){return;})); +REQUIRE(!o35r); + +const tl::optional o36 = tl::nullopt; +auto o36r = std::move(o36).map([](int){return 42;}); +REQUIRE(!o36r); + +const tl::optional o37 = tl::nullopt; +auto o37r = std::move(o37).map([](int){return;}); +REQUIRE(!o37r); + +const tl::optional o38 = tl::nullopt; +auto o38r = std::move(o38).map(tl::make_optional([](int){return 42;})); +REQUIRE(!o38r); + +const tl::optional o39 = tl::nullopt; +auto o39r = std::move(o39).map(tl::make_optional([](int){return;})); +REQUIRE(!o39r); } + +SECTION("map constexpr") { +// test each overload in turn +constexpr tl::optional o16 = 42; +constexpr auto o16r = o16.map(get_int); +STATIC_REQUIRE(*o16r == 42); + +constexpr tl::optional o18 = 42; +constexpr auto opt_int = tl::make_optional(get_int); +constexpr auto o18r = o18.map(opt_int); +STATIC_REQUIRE(*o18r == 42); + +constexpr tl::optional o20 = 42; +constexpr auto o20r = std::move(o20).map(get_int); +STATIC_REQUIRE(*o20r == 42); + +constexpr tl::optional o22 = 42; +constexpr auto o22r = std::move(o22).map(opt_int); +STATIC_REQUIRE(*o22r == 42); + +constexpr tl::optional o32 = tl::nullopt; +constexpr auto o32r = o32.map(get_int); +STATIC_REQUIRE(!o32r); + +constexpr tl::optional o34 = tl::nullopt; +constexpr auto o34r = o34.map(opt_int); +STATIC_REQUIRE(!o34r); + +constexpr tl::optional o36 = tl::nullopt; +constexpr auto o36r = std::move(o36).map(get_int); +STATIC_REQUIRE(!o36r); + +constexpr tl::optional o38 = tl::nullopt; +constexpr auto o38r = std::move(o38).map(opt_int); +STATIC_REQUIRE(!o38r); + } + SECTION("bind") { // lhs is empty @@ -85,7 +255,7 @@ SECTION("bind") { REQUIRE(!o4r); struct rval_call_bind { - auto operator()(int) && { return tl::optional(42.0); }; + tl::optional operator()(int) && { return tl::optional(42.0); }; }; // ensure that function object is forwarded @@ -106,6 +276,122 @@ SECTION("bind") { auto o7r = o7.bind([](const int &i) { return tl::optional(i); }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o7r.value() == 42); + + // test each overload in turn + tl::optional o8 = 42; + auto o8r = o8.bind([](int i) { return tl::make_optional(42); }); + REQUIRE(*o8r == 42); + + tl::optional o9 = 42; + auto o9r = std::move(o9).bind([](int i) { return tl::make_optional(42); }); + REQUIRE(*o9r == 42); + + const tl::optional o10 = 42; + auto o10r = o10.bind([](int i) { return tl::make_optional(42); }); + REQUIRE(*o10r == 42); + + const tl::optional o11 = 42; + auto o11r = std::move(o11).bind([](int i) { return tl::make_optional(42); }); + REQUIRE(*o11r == 42); + + tl::optional o12 = 42; + auto o12r = o12.bind([](int i) { return tl::make_optional(42); }, []{return;}); + REQUIRE(*o12r == 42); + + tl::optional o13 = 42; + auto o13r = std::move(o13).bind([](int i) { return tl::make_optional(42); }, []{return;}); + REQUIRE(*o13r == 42); + + const tl::optional o14 = 42; + auto o14r = o14.bind([](int i) { return tl::make_optional(42); }, []{return;}); + REQUIRE(*o14r == 42); + + const tl::optional o15 = 42; + auto o15r = std::move(o15).bind([](int i) { return tl::make_optional(42); }, []{return;}); + REQUIRE(*o15r == 42); + + tl::optional o16 = tl::nullopt; + auto o16r = o16.bind([](int i) { return tl::make_optional(42); }); + REQUIRE(!o16r); + + tl::optional o17 = tl::nullopt; + auto o17r = std::move(o17).bind([](int i) { return tl::make_optional(42); }); + REQUIRE(!o17r); + + const tl::optional o18 = tl::nullopt; + auto o18r = o18.bind([](int i) { return tl::make_optional(42); }); + REQUIRE(!o18r); + + const tl::optional o19 = tl::nullopt; + auto o19r = std::move(o19).bind([](int i) { return tl::make_optional(42); }); + REQUIRE(!o19r); + + tl::optional o20 = tl::nullopt; + bool c20 = false; + auto o20r = o20.bind([](int i) { return tl::make_optional(42); }, [&]{c20 = true;}); + REQUIRE(!o20r); + REQUIRE(!c20); + + tl::optional o21 = tl::nullopt; + bool c21 = false; + auto o21r = std::move(o21).bind([](int i) { return tl::make_optional(42); }, [&]{c21 = true;}); + REQUIRE(!o21r); + REQUIRE(!c21); + + const tl::optional o22 = tl::nullopt; + bool c22 = false; + auto o22r = o22.bind([](int i) { return tl::make_optional(42); }, [&]{c22 = true;}); + REQUIRE(!o22r); + REQUIRE(!c22); + + const tl::optional o23 = tl::nullopt; + bool c23 = false; + auto o23r = std::move(o23).bind([](int i) { return tl::make_optional(42); }, [&]{c23 = true;}); + REQUIRE(!o23r); + REQUIRE(!c23); + + tl::optional o24 = 42; + bool c24 = false; + auto o24r = o24.bind([](int i) { return tl::optional{tl::nullopt}; }, [&]{c24 = true;}); + REQUIRE(!o24r); + REQUIRE(c24); + + tl::optional o25 = 42; + bool c25 = false; + auto o25r = std::move(o25).bind([](int i) { return tl::optional{tl::nullopt}; }, [&]{c25 = true;}); + REQUIRE(!o25r); + REQUIRE(c25); + + const tl::optional o26 = 42; + bool c26 = false; + auto o26r = o26.bind([](int i) { return tl::optional{tl::nullopt};}, [&]{c26 = true;}); + REQUIRE(!o26r); + REQUIRE(c26); + + const tl::optional o27 = 42; + bool c27 = false; + auto o27r = std::move(o27).bind([](int i) { return tl::optional{tl::nullopt};}, [&]{c27 = true;}); + REQUIRE(!o27r); + REQUIRE(c27); + +} + +SECTION("constexpr bind") { + constexpr tl::optional o10 = 42; + constexpr auto o10r = o10.bind(get_opt_int); + REQUIRE(*o10r == 42); + + constexpr tl::optional o11 = 42; + constexpr auto o11r = std::move(o11).bind(get_opt_int); + REQUIRE(*o11r == 42); + + constexpr tl::optional o18 = tl::nullopt; + constexpr auto o18r = o18.bind(get_opt_int); + REQUIRE(!o18r); + + constexpr tl::optional o19 = tl::nullopt; + constexpr auto o19r = std::move(o19).bind(get_opt_int); + REQUIRE(!o19r); } } ;