// { dg-options "-std=gnu++23" } // { dg-do run { target c++23 } } #include #if __cpp_lib_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() ); void f(int&) { } void test_unconstrained() { // PR libstdc++/102863 - Optional monadic ops should not be constrained std::optional x; auto answer = x.and_then([](auto& y) { f(y); return std::optional{42}; }); VERIFY( !answer ); } int main() { test_and_then(); test_forwarding(); test_unconstrained(); }