[libcxx-commits] [libcxx] [libc++] Fix bug where optional<T&> couldn't be constructed from a monadic operation (PR #203462)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jun 11 23:17:30 PDT 2026


================
@@ -571,6 +571,14 @@ struct __optional_storage_base<_Tp, true> {
     __convert_init_ref_val(std::forward<_UArg>(__uarg));
   }
 
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+    _Tp& __r = std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...);
----------------
frederick-vs-ja wrote:

Shouldn't this be direct-non-list-initialization? See [[optional.monadic]](https://eel.is/c++draft/optional.monadic), [[optional.ref.monadic]](https://eel.is/c++draft/optional.ref.monadic).

```suggestion
    _Tp& __r(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...));
```

The difference is quite observable. [Godbolt link](https://godbolt.org/z/xcxKPsrsE). Perhaps we should add new test cases to ensure that copy-initialization isn't accidentally used.
```C++
struct X {
  explicit operator int&() const;
};

struct Y {
  template <class = void>
  operator int&() const;
  explicit operator int&() const = delete;
};

int& r1 = X{}; // error
int& r2(X{});  // OK
int& r3 = Y{}; // OK
int& r4(Y{});  // error
```

https://github.com/llvm/llvm-project/pull/203462


More information about the libcxx-commits mailing list