[libcxx-commits] [PATCH] D154116: [libc++] Implement LWG3938 (Cannot use std::expected monadic ops with move-only error_type)

Yurong via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jul 5 10:38:40 PDT 2023


yronglin added inline comments.


================
Comment at: libcxx/include/__expected/expected.h:694
   template <class _Func>
     requires is_constructible_v<_Tp, _Tp&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & {
----------------
yronglin wrote:
> yronglin wrote:
> > Mordante wrote:
> > > Can you update this to the Standard wording too? the same for the other places.
> > I tried to keep the same with the std wording, but it compile failed:
> > ```
> > error: invalid use of 'this' outside of a non-static member function
> >   694 |     requires is_constructible_v<_Tp, decltype(**this)>
> >       |                                                 ^
> > ```
> As we talked in discord:
> 
> 1. `*this` always produces an LValue
> 
> ```
>   template <class _Func>
>     requires is_constructible_v<_Err, const _Err&&>
>   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
>     static_assert(std::is_same_v<decltype(*this), const std::expected<_Tp, _Err>&&>);
>     static_assert(std::is_same_v<decltype(**this), const _Tp&&>);
>     using _Up = remove_cvref_t<invoke_result_t<_Func, decltype(**this)>>;
>     static_assert(
>         __is_std_expected<_Up>::value, "The result of f(std::move(value())) must be a specialization of std::expected");
>     static_assert(is_same_v<typename _Up::error_type, _Err>,
>                   "The result of f(std::move(value())) must have the same error_type as this expected");
>     if (has_value()) {
>       return std::invoke(std::forward<_Func>(__f), std::move(**this));
>     }
>     return _Up(unexpect, std::move(error()));
>   }
> ```
> the two `static_assert` fails:
> ```
>  static_assert(std::is_same_v<decltype(*this), const std::expected<_Tp, _Err>&&>);
>  static_assert(std::is_same_v<decltype(**this), const _Tp&&>);
> ```
> when `and_then` called by:
> ```
> #include <expected>
> #include <cassert>
> 
> struct UnexpectedCRVal {
>   std::expected<int, int> operator()(int&)       = delete;
>   std::expected<int, int> operator()(const int&) = delete;
>   std::expected<int, int> operator()(int&&)      = delete;
>   constexpr std::expected<int, int> operator()(const int&&) { return std::expected<int, int>(std::unexpected<int>(5)); }
> };
> 
> int main() {
>     const std::expected<int, int> e{0};
>     assert(std::move(e).and_then(UnexpectedCRVal{}).error() == 5);
>     return 0;
> }
> ```
> 
> The reason is that: `*this` always produces an Lvalue http://eel.is/c++draft/expr.unary.op#1.sentence-3 , so the type of `decltype(**this)` is `const _Tp &` but not `const _Tp &&` 
> 
> 2. we can use no-static member function in constraints
> 
> std wording: `Constraints: is_constructible_v<E, decltype(std​::​move(error()))> is true.`
> we can't use `decltype(std​::​move(error()))` outside a non-static member function, and we use `const _Err&&` as a workaround before.
> Also `is_constructible_v<decltype(**this)>` in constraints has the same issue:
> ```
> error: invalid use of 'this' outside of a non-static member function
>   694 |     requires is_constructible_v<_Tp, decltype(**this)>
>           |      
> ```
Nit:`s/discord/Discord`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D154116/new/

https://reviews.llvm.org/D154116



More information about the libcxx-commits mailing list