From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id AE1A23858D39; Tue, 19 Oct 2021 14:01:47 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AE1A23858D39 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r12-4506] libstdc++: Implement monadic operations for std::optional (P0798R8) X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/master X-Git-Oldrev: 6920d5a1a2834e9c62d441b8f4c6186b01107d13 X-Git-Newrev: 82b2e4f8cf5a01c6724fe3f465a77ee03cfcaae2 Message-Id: <20211019140147.AE1A23858D39@sourceware.org> Date: Tue, 19 Oct 2021 14:01:47 +0000 (GMT) X-BeenThere: libstdc++-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 19 Oct 2021 14:01:47 -0000 https://gcc.gnu.org/g:82b2e4f8cf5a01c6724fe3f465a77ee03cfcaae2 commit r12-4506-g82b2e4f8cf5a01c6724fe3f465a77ee03cfcaae2 Author: Jonathan Wakely Date: Tue Oct 19 11:06:56 2021 +0100 libstdc++: Implement monadic operations for std::optional (P0798R8) Another new addition to the C++23 working draft. The new member functions of std::optional are only defined for C++23, but the new members of _Optional_payload_base are defined for C++20 so that they can be used in non-propagating-cache in . The _Optional_payload_base::_M_construct member can also be used in non-propagating-cache now, because it's constexpr since r12-4389. There will be an LWG issue about the feature test macro, suggesting that we should just bump the value of __cpp_lib_optional instead. I haven't done that here, but it can be changed once consensus is reached on the change. libstdc++-v3/ChangeLog: * include/std/optional (_Optional_payload_base::_Storage): Add constructor taking a callable function to invoke. (_Optional_payload_base::_M_apply): New function. (__cpp_lib_monadic_optional): Define for C++23. (optional::and_then, optional::transform, optional::or_else): Define for C++23. * include/std/ranges (__detail::__cached): Remove. (__detail::__non_propagating_cache): Remove use of __cached for contained value. Use _Optional_payload_base::_M_construct and _Optional_payload_base::_M_apply to set the contained value. * include/std/version (__cpp_lib_monadic_optional): Define. * testsuite/20_util/optional/monadic/and_then.cc: New test. * testsuite/20_util/optional/monadic/or_else.cc: New test. * testsuite/20_util/optional/monadic/or_else_neg.cc: New test. * testsuite/20_util/optional/monadic/transform.cc: New test. * testsuite/20_util/optional/monadic/version.cc: New test. Diff: --- libstdc++-v3/include/std/optional | 182 ++++++++++++++++++++- libstdc++-v3/include/std/ranges | 42 +---- libstdc++-v3/include/std/version | 3 + .../testsuite/20_util/optional/monadic/and_then.cc | 120 ++++++++++++++ .../testsuite/20_util/optional/monadic/or_else.cc | 103 ++++++++++++ .../20_util/optional/monadic/or_else_neg.cc | 30 ++++ .../20_util/optional/monadic/transform.cc | 123 ++++++++++++++ .../testsuite/20_util/optional/monadic/version.cc | 10 ++ 8 files changed, 573 insertions(+), 40 deletions(-) diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index b69268b3642..eac91d3c160 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -1,6 +1,7 @@ // -*- C++ -*- // Copyright (C) 2013-2021 Free Software Foundation, Inc. +// Copyright The GNU Toolchain Authors. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the @@ -44,6 +45,10 @@ #include // in_place_t #if __cplusplus > 201703L # include +# include // std::__invoke +#endif +#if __cplusplus > 202002L +# include #endif namespace std _GLIBCXX_VISIBILITY(default) @@ -81,6 +86,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Tag to disengage optional objects. inline constexpr nullopt_t nullopt { nullopt_t::_Construct::_Token }; + template struct _Optional_func { _Fn& _M_f; }; + /** * @brief Exception class thrown when a disengaged optional object is * dereferenced. @@ -211,6 +218,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_value(__il, std::forward<_Args>(__args)...) { } +#if __cplusplus >= 202002L + template + constexpr + _Storage(_Optional_func<_Fn> __f, _Arg&& __arg) + : _M_value(std::__invoke(std::forward<_Fn>(__f._M_f), + std::forward<_Arg>(__arg))) + { } +#endif + _Empty_byte _M_empty; _Up _M_value; }; @@ -232,6 +248,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_value(__il, std::forward<_Args>(__args)...) { } +#if __cplusplus >= 202002L + template + constexpr + _Storage(_Optional_func<_Fn> __f, _Arg&& __arg) + : _M_value(std::__invoke(std::forward<_Fn>(__f._M_f), + std::forward<_Arg>(__arg))) + { } +#endif + // User-provided destructor is needed when _Up has non-trivial dtor. _GLIBCXX20_CONSTEXPR ~_Storage() { } @@ -260,6 +285,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_payload._M_value.~_Stored_type(); } +#if __cplusplus >= 202002L + template + constexpr void + _M_apply(_Optional_func<_Fn> __f, _Up&& __x) + { + std::construct_at(std::__addressof(this->_M_payload), + __f, std::forward<_Up>(__x)); + _M_engaged = true; + } +#endif + // The _M_get() operations have _M_engaged as a precondition. // They exist to access the contained value with the appropriate // const-qualification, because _M_payload has had the const removed. @@ -637,6 +673,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template class optional; + template + inline constexpr bool __is_optional_v = false; + template + inline constexpr bool __is_optional_v> = true; + template using __converts_from_optional = __or_&>, @@ -1002,7 +1043,143 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return static_cast<_Tp>(std::forward<_Up>(__u)); } +#if __cplusplus > 202002L && __cpp_lib_concepts +#define __cpp_lib_monadic_optional 202110L + + // [optional.monadic] + + template requires invocable<_Fn, _Tp&> + constexpr auto + and_then(_Fn&& __f) & + { + using _Up = remove_cvref_t>; + static_assert(__is_optional_v>); + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), **this); + else + return _Up(); + } + + template requires invocable<_Fn, const _Tp&> + constexpr auto + and_then(_Fn&& __f) const & + { + using _Up = remove_cvref_t>; + static_assert(__is_optional_v<_Up>); + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), **this); + else + return _Up(); + } + + template requires invocable<_Fn, _Tp> + constexpr auto + and_then(_Fn&& __f) && + { + using _Up = remove_cvref_t>; + static_assert(__is_optional_v>); + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), std::move(**this)); + else + return _Up(); + } + + template requires invocable<_Fn, const _Tp> + constexpr auto + and_then(_Fn&& __f) const && + { + using _Up = remove_cvref_t>; + static_assert(__is_optional_v>); + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), std::move(**this)); + else + return _Up(); + } + + template requires invocable<_Fn, _Tp&> + constexpr auto + transform(_Fn&& __f) & + { + using _Up = invoke_result_t<_Fn, _Tp&>; + if (has_value()) + return optional<_Up>(_Optional_func<_Fn>{__f}, **this); + else + return optional<_Up>(); + } + + template requires invocable<_Fn, const _Tp&> + constexpr auto + transform(_Fn&& __f) const & + { + using _Up = invoke_result_t<_Fn, const _Tp&>; + if (has_value()) + return optional<_Up>(_Optional_func<_Fn>{__f}, **this); + else + return optional<_Up>(); + } + + template requires invocable<_Fn, _Tp> + constexpr auto + transform(_Fn&& __f) && + { + using _Up = invoke_result_t<_Fn, _Tp>; + if (has_value()) + return optional<_Up>(_Optional_func<_Fn>{__f}, std::move(**this)); + else + return optional<_Up>(); + } + + template requires invocable<_Fn, const _Tp> + constexpr auto + transform(_Fn&& __f) const && + { + using _Up = invoke_result_t<_Fn, const _Tp>; + if (has_value()) + return optional<_Up>(_Optional_func<_Fn>{__f}, std::move(**this)); + else + return optional<_Up>(); + } + + template requires invocable<_Fn> && copy_constructible<_Tp> + constexpr optional + or_else(_Fn&& __f) const& + { + using _Up = invoke_result_t<_Fn>; + static_assert( is_same_v, optional> ); + + if (has_value()) + return *this; + else + return std::forward<_Fn>(__f)(); + } + + template requires invocable<_Fn> && move_constructible<_Tp> + constexpr optional + or_else(_Fn&& __f) && + { + using _Up = invoke_result_t<_Fn>; + static_assert( is_same_v, optional> ); + + if (has_value()) + return std::move(*this); + else + return std::forward<_Fn>(__f)(); + } +#endif + _GLIBCXX20_CONSTEXPR void reset() noexcept { this->_M_reset(); } + + private: +#if __cplusplus >= 202002L + template friend class optional; + + template + explicit constexpr + optional(_Optional_func<_Fn> __f, _Value&& __v) + { + this->_M_payload._M_apply(__f, std::forward<_Value>(__v)); + } +#endif }; template @@ -1241,11 +1418,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return !__rhs || __lhs >= *__rhs; } #ifdef __cpp_lib_three_way_comparison - template - inline constexpr bool __is_optional_v = false; - template - inline constexpr bool __is_optional_v> = true; - template requires (!__is_optional_v<_Up>) && three_way_comparable_with<_Tp, _Up> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index b8de400dfbb..18bd087985c 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -1159,34 +1159,10 @@ namespace views::__adaptor // (such as join_view::_M_inner). }; - template - struct __cached - { - struct _Deref_t { }; - static constexpr _Deref_t __deref{}; - - // Initialize _M_t directly from the result of dereferencing __i. - // This avoids any unwanted temporary materialization that would - // occur if *__i was bound to a reference before initializing _M_t. - template - constexpr explicit - __cached(_Deref_t, _Iter&& __i) - : _M_t(*__i) - { } - - template - constexpr explicit - __cached(_Args&&... __args) - : _M_t(std::forward<_Args>(__args)...) - { } - - _Tp _M_t; - }; - template requires is_object_v<_Tp> struct __non_propagating_cache<_Tp> - : protected _Optional_base<__cached<_Tp>> + : protected _Optional_base<_Tp> { __non_propagating_cache() = default; @@ -1218,9 +1194,7 @@ namespace views::__adaptor operator=(_Tp __val) { this->_M_reset(); - std::construct_at(std::__addressof(this->_M_payload._M_payload), - std::in_place, std::move(__val)); - this->_M_payload._M_engaged = true; + this->_M_payload._M_construct(std::move(__val)); return *this; } @@ -1230,22 +1204,20 @@ namespace views::__adaptor constexpr _Tp& operator*() noexcept - { return this->_M_get()._M_t; } + { return this->_M_get(); } constexpr const _Tp& operator*() const noexcept - { return this->_M_get()._M_t; } + { return this->_M_get(); } template constexpr _Tp& _M_emplace_deref(const _Iter& __i) { this->_M_reset(); - // Use the special constructor of __cached<_Tp> that does *__i. - std::construct_at(std::__addressof(this->_M_payload._M_payload), - std::in_place, __cached<_Tp>::__deref, __i); - this->_M_payload._M_engaged = true; - return **this; + auto __f = [] (auto& __x) { return *__x; }; + this->_M_payload._M_apply(_Optional_func{__f}, __i); + return this->_M_get(); } }; diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 2b118301da7..0a7b28abee6 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -292,6 +292,9 @@ #define __cpp_lib_adaptor_iterator_pair_constructor 202106L #define __cpp_lib_invoke_r 202106L #define __cpp_lib_is_scoped_enum 202011L +#if __cpp_lib_concepts +# define __cpp_lib_monadic_optional 202110L +#endif #define __cpp_lib_move_only_function 202110L #define __cpp_lib_string_contains 202011L #if _GLIBCXX_USE_CXX11_ABI // Only supported with cxx11-abi diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/and_then.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/and_then.cc new file mode 100644 index 00000000000..02dcafe6c58 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/and_then.cc @@ -0,0 +1,120 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include + +#ifndef __cpp_lib_monadic_optional +# error "Feature test macro for monadic optional is missing in " +#elif __cpp_lib_monadic_optional < 202110L +# error "Feature test macro for monadic optional has wrong value in " +#endif + +#include + +constexpr bool +test_and_then() +{ + std::optional o; + auto r = o.and_then([](int) -> std::optional { throw 1; }); + VERIFY( !r.has_value() ); + static_assert( std::is_same_v> ); + + o = 111; + r = o.and_then([](int i) -> std::optional { return {i/10}; }); + VERIFY( *r == 11 ); + + return true; +} + +static_assert( test_and_then() ); + +enum { CalledLvalue = 1, CalledConst = 2, PassedLvalue = 4, PassedConst = 8 }; + +struct F +{ + template + static constexpr std::optional + called_as() + { + int res = 0; + if constexpr (std::is_lvalue_reference_v) + res |= CalledLvalue; + if constexpr (std::is_const_v>) + res |= CalledConst; + + if constexpr (std::is_lvalue_reference_v) + res |= PassedLvalue; + if constexpr (std::is_const_v>) + res |= PassedConst; + + return {res}; + } + + template + constexpr std::optional + operator()(T&&) & + { return called_as(); } + + template + constexpr std::optional + operator()(T&&) const & + { return called_as(); } + + template + constexpr std::optional + operator()(T&&) && + { return called_as(); } + + template + constexpr std::optional + operator()(T&&) const && + { return called_as(); } +}; + +constexpr bool +test_forwarding() +{ + std::optional o = 1; + F f; + + VERIFY( *o.and_then(f) == (PassedLvalue|CalledLvalue) ); + VERIFY( *o.and_then(std::move(f)) == PassedLvalue ); + VERIFY( *std::move(o).and_then(f) == CalledLvalue ); + VERIFY( *std::move(o).and_then(std::move(f)) == 0 ); + + const auto& co = o; + + VERIFY( *co.and_then(f) == (PassedLvalue|PassedConst|CalledLvalue) ); + VERIFY( *co.and_then(std::move(f)) == (PassedLvalue|PassedConst) ); + VERIFY( *std::move(co).and_then(f) == (PassedConst|CalledLvalue) ); + VERIFY( *std::move(co).and_then(std::move(f)) == PassedConst ); + + const auto& cf = f; + + VERIFY( *o.and_then(cf) == (PassedLvalue|CalledLvalue|CalledConst) ); + VERIFY( *o.and_then(std::move(cf)) == (PassedLvalue|CalledConst) ); + VERIFY( *std::move(o).and_then(cf) == (CalledLvalue|CalledConst) ); + VERIFY( *std::move(o).and_then(std::move(cf)) == CalledConst ); + + VERIFY( *co.and_then(cf) == (PassedLvalue|PassedConst|CalledLvalue|CalledConst) ); + VERIFY( *co.and_then(std::move(cf)) == (PassedLvalue|PassedConst|CalledConst) ); + VERIFY( *std::move(co).and_then(cf) == (PassedConst|CalledLvalue|CalledConst) ); + VERIFY( *std::move(co).and_then(std::move(cf)) == (PassedConst|CalledConst) ); + + o = std::nullopt; + + VERIFY( ! o.and_then(f).has_value() ); + VERIFY( ! co.and_then(f).has_value() ); + VERIFY( ! std::move(o).and_then(f).has_value() ); + VERIFY( ! std::move(co).and_then(f).has_value() ); + + return true; +} + +static_assert( test_forwarding() ); + +int main() +{ + test_and_then(); + test_forwarding(); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else.cc new file mode 100644 index 00000000000..05081046c80 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include + +constexpr bool +test_or_else() +{ + std::optional o; + auto&& r = o.or_else([]() -> std::optional { return {303}; }); + VERIFY( !o ); + VERIFY( *r == 303 ); + static_assert( std::is_same_v&&> ); + + o = 808; + const std::optional tr = 909; + auto&& r2 = o.or_else([&]() -> const auto& { return tr; }); + static_assert( std::is_same_v&&> ); + VERIFY( r2 == o ); + + return true; +} + +static_assert( test_or_else() ); + +constexpr bool +test_move() +{ + struct X + { + constexpr X() { } + constexpr X(const X&) { copied = true; } + constexpr X(X&& x) { moved = true; x.gone = true; } + + bool copied = false; + bool moved = false; + bool gone = false; + }; + + std::optional o(std::in_place); + + auto f = []{ return std::optional{}; }; + VERIFY( o.or_else(f)->copied ); + VERIFY( ! o->gone ); + + VERIFY( std::move(o).or_else(f)->moved ); + VERIFY( o->gone ); + + struct move_only + { + constexpr move_only() { } + constexpr move_only(move_only&&) { } + }; + + std::optional mo; + // doesn't require copy + std::move(mo).or_else([] { return std::optional{}; }); + + return true; +} + +static_assert( test_move() ); + +constexpr bool +test_call() +{ + struct F + { + constexpr std::optional operator()() & { return {1}; } + constexpr std::optional operator()() && { return {2}; } + constexpr std::optional operator()() const & { return {3}; }; + constexpr std::optional operator()() const && { return {4}; } + }; + + std::optional o; + F f; + + VERIFY( *o.or_else(f) == 1 ); + VERIFY( *std::move(o).or_else(f) == 1 ); + + VERIFY( *o.or_else(std::move(f)) == 2 ); + VERIFY( *std::move(o).or_else(std::move(f)) == 2 ); + + const F& cf = f; + + VERIFY( *o.or_else(cf) == 3 ); + VERIFY( *std::move(o).or_else(cf) == 3 ); + + VERIFY( *o.or_else(std::move(cf)) == 4 ); + VERIFY( *std::move(o).or_else(std::move(cf)) == 4 ); + + return true; +} + +static_assert( test_call() ); + +int main() +{ + test_or_else(); + test_move(); + test_call(); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc new file mode 100644 index 00000000000..16e94864f3b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc @@ -0,0 +1,30 @@ +// { dg-options "-std=gnu++23" } +// { dg-do compile { target c++23 } } + +#include + +void +test01() +{ + std::optional o; + o.or_else([&] { return o; }); // OK + o.or_else([] { return std::optional(); }); // { dg-error "here" } + o.or_else([] { return 1; }); // { dg-error "here" } + std::move(o).or_else([] { return std::optional(); }); // { dg-error "here" } + std::move(o).or_else([] { return 1; }); // { dg-error "here" } +} + +// { dg-prune-output "static assertion failed" } + +void +test02() +{ + struct move_only + { + move_only() { } + move_only(move_only&&) { } + }; + + std::optional mo; + mo.or_else([]{ return std::optional{}; }); // { dg-error "no matching function" } +} diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/transform.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/transform.cc new file mode 100644 index 00000000000..d01ccb2e0f2 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/transform.cc @@ -0,0 +1,123 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include + +constexpr bool +test_transform() +{ + std::optional o; + auto&& r = o.transform([](int) -> unsigned { throw 1; }); + static_assert( std::is_same_v&&> ); + VERIFY( ! r.has_value() ); + + o = 10; + auto&& r2 = o.transform([](int i) -> unsigned { return i + 2u; }); + static_assert( std::is_same_v&&> ); + VERIFY( *r2 == 12u ); + + return true; +} + +static_assert( test_transform() ); + +enum { CalledLvalue = 1, CalledConst = 2, PassedLvalue = 4, PassedConst = 8 }; + +struct F +{ + template + static constexpr int + called_as() + { + int res = 0; + if constexpr (std::is_lvalue_reference_v) + res |= CalledLvalue; + if constexpr (std::is_const_v>) + res |= CalledConst; + + if constexpr (std::is_lvalue_reference_v) + res |= PassedLvalue; + if constexpr (std::is_const_v>) + res |= PassedConst; + + return res; + } + + template + constexpr int + operator()(T&&) & + { return called_as(); } + + template + constexpr int + operator()(T&&) const & + { return called_as(); } + + template + constexpr int + operator()(T&&) && + { return called_as(); } + + template + constexpr int + operator()(T&&) const && + { return called_as(); } +}; + +constexpr bool +test_forwarding() +{ + std::optional o = 1; + F f; + + VERIFY( *o.transform(f) == (PassedLvalue|CalledLvalue) ); + VERIFY( *o.transform(std::move(f)) == PassedLvalue ); + VERIFY( *std::move(o).transform(f) == CalledLvalue ); + VERIFY( *std::move(o).transform(std::move(f)) == 0 ); + + const auto& co = o; + + VERIFY( *co.transform(f) == (PassedLvalue|PassedConst|CalledLvalue) ); + VERIFY( *co.transform(std::move(f)) == (PassedLvalue|PassedConst) ); + VERIFY( *std::move(co).transform(f) == (PassedConst|CalledLvalue) ); + VERIFY( *std::move(co).transform(std::move(f)) == PassedConst ); + + const auto& cf = f; + + VERIFY( *o.transform(cf) == (PassedLvalue|CalledLvalue|CalledConst) ); + VERIFY( *o.transform(std::move(cf)) == (PassedLvalue|CalledConst) ); + VERIFY( *std::move(o).transform(cf) == (CalledLvalue|CalledConst) ); + VERIFY( *std::move(o).transform(std::move(cf)) == CalledConst ); + + VERIFY( *co.transform(cf) == (PassedLvalue|PassedConst|CalledLvalue|CalledConst) ); + VERIFY( *co.transform(std::move(cf)) == (PassedLvalue|PassedConst|CalledConst) ); + VERIFY( *std::move(co).transform(cf) == (PassedConst|CalledLvalue|CalledConst) ); + VERIFY( *std::move(co).transform(std::move(cf)) == (PassedConst|CalledConst) ); + + o = std::nullopt; + + VERIFY( ! o.transform(f).has_value() ); + VERIFY( ! co.transform(f).has_value() ); + VERIFY( ! std::move(o).transform(f).has_value() ); + VERIFY( ! std::move(co).transform(f).has_value() ); + + return true; +} + +static_assert( test_forwarding() ); + +constexpr bool +test_copy_elision() +{ + return true; +} + +static_assert( test_copy_elision() ); + +int main() +{ + test_transform(); + test_forwarding(); + test_copy_elision(); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/version.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/version.cc new file mode 100644 index 00000000000..90b2a90a5df --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/version.cc @@ -0,0 +1,10 @@ +// { dg-options "-std=gnu++23" } +// { dg-do preprocess { target c++23 } } + +#include + +#ifndef __cpp_lib_monadic_optional +# error "Feature test macro for monadic optional is missing in " +#elif __cpp_lib_monadic_optional < 202110L +# error "Feature test macro for monadic optional has wrong value in " +#endif