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 <ranges>. 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.
This commit is contained in:
parent
6920d5a1a2
commit
82b2e4f8cf
8 changed files with 573 additions and 40 deletions
|
@ -1,6 +1,7 @@
|
|||
// <optional> -*- 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 <bits/utility.h> // in_place_t
|
||||
#if __cplusplus > 201703L
|
||||
# include <compare>
|
||||
# include <bits/invoke.h> // std::__invoke
|
||||
#endif
|
||||
#if __cplusplus > 202002L
|
||||
# include <concepts>
|
||||
#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<typename _Fn> 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<typename _Fn, typename _Arg>
|
||||
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<typename _Fn, typename _Arg>
|
||||
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<typename _Fn, typename _Up>
|
||||
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<typename _Tp>
|
||||
class optional;
|
||||
|
||||
template<typename _Tp>
|
||||
inline constexpr bool __is_optional_v = false;
|
||||
template<typename _Tp>
|
||||
inline constexpr bool __is_optional_v<optional<_Tp>> = true;
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
using __converts_from_optional =
|
||||
__or_<is_constructible<_Tp, const optional<_Up>&>,
|
||||
|
@ -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<typename _Fn> requires invocable<_Fn, _Tp&>
|
||||
constexpr auto
|
||||
and_then(_Fn&& __f) &
|
||||
{
|
||||
using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp&>>;
|
||||
static_assert(__is_optional_v<remove_cvref_t<_Up>>);
|
||||
if (has_value())
|
||||
return std::__invoke(std::forward<_Fn>(__f), **this);
|
||||
else
|
||||
return _Up();
|
||||
}
|
||||
|
||||
template<typename _Fn> requires invocable<_Fn, const _Tp&>
|
||||
constexpr auto
|
||||
and_then(_Fn&& __f) const &
|
||||
{
|
||||
using _Up = remove_cvref_t<invoke_result_t<_Fn, const _Tp&>>;
|
||||
static_assert(__is_optional_v<_Up>);
|
||||
if (has_value())
|
||||
return std::__invoke(std::forward<_Fn>(__f), **this);
|
||||
else
|
||||
return _Up();
|
||||
}
|
||||
|
||||
template<typename _Fn> requires invocable<_Fn, _Tp>
|
||||
constexpr auto
|
||||
and_then(_Fn&& __f) &&
|
||||
{
|
||||
using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp>>;
|
||||
static_assert(__is_optional_v<remove_cvref_t<_Up>>);
|
||||
if (has_value())
|
||||
return std::__invoke(std::forward<_Fn>(__f), std::move(**this));
|
||||
else
|
||||
return _Up();
|
||||
}
|
||||
|
||||
template<typename _Fn> requires invocable<_Fn, const _Tp>
|
||||
constexpr auto
|
||||
and_then(_Fn&& __f) const &&
|
||||
{
|
||||
using _Up = remove_cvref_t<invoke_result_t<_Fn, const _Tp>>;
|
||||
static_assert(__is_optional_v<remove_cvref_t<_Up>>);
|
||||
if (has_value())
|
||||
return std::__invoke(std::forward<_Fn>(__f), std::move(**this));
|
||||
else
|
||||
return _Up();
|
||||
}
|
||||
|
||||
template<typename _Fn> 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<typename _Fn> 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<typename _Fn> 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<typename _Fn> 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<typename _Fn> requires invocable<_Fn> && copy_constructible<_Tp>
|
||||
constexpr optional
|
||||
or_else(_Fn&& __f) const&
|
||||
{
|
||||
using _Up = invoke_result_t<_Fn>;
|
||||
static_assert( is_same_v<remove_cvref_t<_Up>, optional> );
|
||||
|
||||
if (has_value())
|
||||
return *this;
|
||||
else
|
||||
return std::forward<_Fn>(__f)();
|
||||
}
|
||||
|
||||
template<typename _Fn> requires invocable<_Fn> && move_constructible<_Tp>
|
||||
constexpr optional
|
||||
or_else(_Fn&& __f) &&
|
||||
{
|
||||
using _Up = invoke_result_t<_Fn>;
|
||||
static_assert( is_same_v<remove_cvref_t<_Up>, 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<typename _Up> friend class optional;
|
||||
|
||||
template<typename _Fn, typename _Value>
|
||||
explicit constexpr
|
||||
optional(_Optional_func<_Fn> __f, _Value&& __v)
|
||||
{
|
||||
this->_M_payload._M_apply(__f, std::forward<_Value>(__v));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
|
@ -1241,11 +1418,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
{ return !__rhs || __lhs >= *__rhs; }
|
||||
|
||||
#ifdef __cpp_lib_three_way_comparison
|
||||
template<typename _Tp>
|
||||
inline constexpr bool __is_optional_v = false;
|
||||
template<typename _Tp>
|
||||
inline constexpr bool __is_optional_v<optional<_Tp>> = true;
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
requires (!__is_optional_v<_Up>)
|
||||
&& three_way_comparable_with<_Tp, _Up>
|
||||
|
|
|
@ -1159,34 +1159,10 @@ namespace views::__adaptor
|
|||
// (such as join_view::_M_inner).
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
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<typename _Iter>
|
||||
constexpr explicit
|
||||
__cached(_Deref_t, _Iter&& __i)
|
||||
: _M_t(*__i)
|
||||
{ }
|
||||
|
||||
template<typename... _Args>
|
||||
constexpr explicit
|
||||
__cached(_Args&&... __args)
|
||||
: _M_t(std::forward<_Args>(__args)...)
|
||||
{ }
|
||||
|
||||
_Tp _M_t;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
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<typename _Iter>
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
120
libstdc++-v3/testsuite/20_util/optional/monadic/and_then.cc
Normal file
120
libstdc++-v3/testsuite/20_util/optional/monadic/and_then.cc
Normal file
|
@ -0,0 +1,120 @@
|
|||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <optional>
|
||||
|
||||
#ifndef __cpp_lib_monadic_optional
|
||||
# error "Feature test macro for monadic optional is missing in <optional>"
|
||||
#elif __cpp_lib_monadic_optional < 202110L
|
||||
# error "Feature test macro for monadic optional has wrong value in <optional>"
|
||||
#endif
|
||||
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
constexpr bool
|
||||
test_and_then()
|
||||
{
|
||||
std::optional<int> o;
|
||||
auto r = o.and_then([](int) -> std::optional<short> { throw 1; });
|
||||
VERIFY( !r.has_value() );
|
||||
static_assert( std::is_same_v<decltype(r), std::optional<short>> );
|
||||
|
||||
o = 111;
|
||||
r = o.and_then([](int i) -> std::optional<short> { 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<typename This, typename Value>
|
||||
static constexpr std::optional<int>
|
||||
called_as()
|
||||
{
|
||||
int res = 0;
|
||||
if constexpr (std::is_lvalue_reference_v<This>)
|
||||
res |= CalledLvalue;
|
||||
if constexpr (std::is_const_v<std::remove_reference_t<This>>)
|
||||
res |= CalledConst;
|
||||
|
||||
if constexpr (std::is_lvalue_reference_v<Value>)
|
||||
res |= PassedLvalue;
|
||||
if constexpr (std::is_const_v<std::remove_reference_t<Value>>)
|
||||
res |= PassedConst;
|
||||
|
||||
return {res};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr std::optional<int>
|
||||
operator()(T&&) &
|
||||
{ return called_as<F&, T>(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr std::optional<int>
|
||||
operator()(T&&) const &
|
||||
{ return called_as<const F&, T>(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr std::optional<int>
|
||||
operator()(T&&) &&
|
||||
{ return called_as<F, T>(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr std::optional<int>
|
||||
operator()(T&&) const &&
|
||||
{ return called_as<const F, T>(); }
|
||||
};
|
||||
|
||||
constexpr bool
|
||||
test_forwarding()
|
||||
{
|
||||
std::optional<long> 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();
|
||||
}
|
103
libstdc++-v3/testsuite/20_util/optional/monadic/or_else.cc
Normal file
103
libstdc++-v3/testsuite/20_util/optional/monadic/or_else.cc
Normal file
|
@ -0,0 +1,103 @@
|
|||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <optional>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
constexpr bool
|
||||
test_or_else()
|
||||
{
|
||||
std::optional<int> o;
|
||||
auto&& r = o.or_else([]() -> std::optional<int> { return {303}; });
|
||||
VERIFY( !o );
|
||||
VERIFY( *r == 303 );
|
||||
static_assert( std::is_same_v<decltype(r), std::optional<int>&&> );
|
||||
|
||||
o = 808;
|
||||
const std::optional<int> tr = 909;
|
||||
auto&& r2 = o.or_else([&]() -> const auto& { return tr; });
|
||||
static_assert( std::is_same_v<decltype(r2), std::optional<int>&&> );
|
||||
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<X> o(std::in_place);
|
||||
|
||||
auto f = []{ return std::optional<X>{}; };
|
||||
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<move_only> mo;
|
||||
// doesn't require copy
|
||||
std::move(mo).or_else([] { return std::optional<move_only>{}; });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert( test_move() );
|
||||
|
||||
constexpr bool
|
||||
test_call()
|
||||
{
|
||||
struct F
|
||||
{
|
||||
constexpr std::optional<int> operator()() & { return {1}; }
|
||||
constexpr std::optional<int> operator()() && { return {2}; }
|
||||
constexpr std::optional<int> operator()() const & { return {3}; };
|
||||
constexpr std::optional<int> operator()() const && { return {4}; }
|
||||
};
|
||||
|
||||
std::optional<int> 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();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do compile { target c++23 } }
|
||||
|
||||
#include <optional>
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
std::optional<int> o;
|
||||
o.or_else([&] { return o; }); // OK
|
||||
o.or_else([] { return std::optional<short>(); }); // { dg-error "here" }
|
||||
o.or_else([] { return 1; }); // { dg-error "here" }
|
||||
std::move(o).or_else([] { return std::optional<short>(); }); // { 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<move_only> mo;
|
||||
mo.or_else([]{ return std::optional<move_only>{}; }); // { dg-error "no matching function" }
|
||||
}
|
123
libstdc++-v3/testsuite/20_util/optional/monadic/transform.cc
Normal file
123
libstdc++-v3/testsuite/20_util/optional/monadic/transform.cc
Normal file
|
@ -0,0 +1,123 @@
|
|||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <optional>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
constexpr bool
|
||||
test_transform()
|
||||
{
|
||||
std::optional<int> o;
|
||||
auto&& r = o.transform([](int) -> unsigned { throw 1; });
|
||||
static_assert( std::is_same_v<decltype(r), std::optional<unsigned>&&> );
|
||||
VERIFY( ! r.has_value() );
|
||||
|
||||
o = 10;
|
||||
auto&& r2 = o.transform([](int i) -> unsigned { return i + 2u; });
|
||||
static_assert( std::is_same_v<decltype(r2), std::optional<unsigned>&&> );
|
||||
VERIFY( *r2 == 12u );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert( test_transform() );
|
||||
|
||||
enum { CalledLvalue = 1, CalledConst = 2, PassedLvalue = 4, PassedConst = 8 };
|
||||
|
||||
struct F
|
||||
{
|
||||
template<typename This, typename Value>
|
||||
static constexpr int
|
||||
called_as()
|
||||
{
|
||||
int res = 0;
|
||||
if constexpr (std::is_lvalue_reference_v<This>)
|
||||
res |= CalledLvalue;
|
||||
if constexpr (std::is_const_v<std::remove_reference_t<This>>)
|
||||
res |= CalledConst;
|
||||
|
||||
if constexpr (std::is_lvalue_reference_v<Value>)
|
||||
res |= PassedLvalue;
|
||||
if constexpr (std::is_const_v<std::remove_reference_t<Value>>)
|
||||
res |= PassedConst;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int
|
||||
operator()(T&&) &
|
||||
{ return called_as<F&, T>(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr int
|
||||
operator()(T&&) const &
|
||||
{ return called_as<const F&, T>(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr int
|
||||
operator()(T&&) &&
|
||||
{ return called_as<F, T>(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr int
|
||||
operator()(T&&) const &&
|
||||
{ return called_as<const F, T>(); }
|
||||
};
|
||||
|
||||
constexpr bool
|
||||
test_forwarding()
|
||||
{
|
||||
std::optional<long> 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();
|
||||
}
|
10
libstdc++-v3/testsuite/20_util/optional/monadic/version.cc
Normal file
10
libstdc++-v3/testsuite/20_util/optional/monadic/version.cc
Normal file
|
@ -0,0 +1,10 @@
|
|||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do preprocess { target c++23 } }
|
||||
|
||||
#include <version>
|
||||
|
||||
#ifndef __cpp_lib_monadic_optional
|
||||
# error "Feature test macro for monadic optional is missing in <version>"
|
||||
#elif __cpp_lib_monadic_optional < 202110L
|
||||
# error "Feature test macro for monadic optional has wrong value in <version>"
|
||||
#endif
|
Loading…
Add table
Reference in a new issue