[libcxx-commits] [libcxx] [libc++] P0792R14: `std::function_ref` (PR #94894)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Jun 9 02:49:30 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Xiaoyang Liu (xiaoyang-sde)

<details>
<summary>Changes</summary>

## Introduction

This patch attempts to implement `std::function_ref` from [P0792R14](https://wg21.link/P0792R14). `std::function_ref` is a non-owning function wrapper that can store and invoke references to functions and function objects, but it can't store pointers to member functions or pointers to member objects. However, it can accept pointers to member functions and pointers to member objects as non-type template parameters.

## Implementation

The implementation follows the specified wording, while incorporating ideas from both #<!-- -->94670 and the [reference implementation](https://github.com/zhihaoy/nontype_functional/blob/main/include/std23/function_ref.h) mentioned in [P0792R14](https://wg21.link/P0792R14).

Similar to `std::move_only_function` and `std::copyable_function`, `std::function_ref` supports function signatures with `const` qualifiers and `noexcept` specifiers, such as `std::function_ref<void(bool) const noexcept>`. This means that `std::function_ref` must be specialized for each combination of qualifier and specifier. To avoid duplicating code, these specializations can be implemented using either preprocessor directives or metaprogramming techniques. The method should align with the implementation approach of `std::move_only_function`, which is still being developed in issue #<!-- -->94670.

## Reference

- [P0792R14: `function_ref`: a type-erased callable reference](https://wg21.link/P0792R14)
- [[func.wrap.ref]](https://eel.is/c++draft/func.wrap.ref)
- [zhihaoy/nontype_functional](https://github.com/zhihaoy/nontype_functional)


---

Patch is 36.79 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94894.diff


15 Files Affected:

- (modified) libcxx/include/CMakeLists.txt (+4) 
- (added) libcxx/include/__functional/function_ref.h (+46) 
- (added) libcxx/include/__functional/function_ref_common.h (+72) 
- (added) libcxx/include/__functional/function_ref_impl.h (+179) 
- (added) libcxx/include/__utility/nontype.h (+34) 
- (modified) libcxx/include/functional (+1) 
- (modified) libcxx/include/module.modulemap (+2) 
- (modified) libcxx/include/utility (+10) 
- (modified) libcxx/modules/std/functional.inc (+4) 
- (modified) libcxx/modules/std/utility.inc (+5) 
- (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp (+84) 
- (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp (+124) 
- (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp (+124) 
- (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp (+119) 
- (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp (+119) 


``````````diff
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cfe1f44777bca..6dd28b79117af 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -393,6 +393,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
+  __functional/function_ref.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
@@ -855,6 +858,7 @@ set(files
   __utility/is_valid_range.h
   __utility/move.h
   __utility/no_destroy.h
+  __utility/nontype.h
   __utility/pair.h
   __utility/piecewise_construct.h
   __utility/priority_tag.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..01cc22a64018e
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+// NOLINTBEGIN(readability-duplicate-include)
+
+#define _LIBCPP_FUNCTION_REF_CV
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV const
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV const
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+// NOLINTEND(readability-duplicate-include)
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..d34a47286cf58
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/nontype.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class>
+inline constexpr bool __is_function_ref = false;
+
+template <class _Rp, class... _ArgTypes>
+inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
+  using type = _Rp(_ArgTypes...);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
+  using type = _Rp(_ArgTypes...) noexcept;
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type = invoke_result_t<_Mp _Gp::*, _Tp&>();
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Fn>
+  requires is_function_v<remove_pointer_t<decltype(_Fn)>>
+function_ref(nontype_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
+
+template <auto _Fn, class _Tp>
+function_ref(nontype_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..2e6ed45f32b19
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,179 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/nontype.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Rp, class... _ArgTypes>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT)> {
+private:
+#  if _LIBCPP_FUNCTION_REF_NOEXCEPT == true
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using = is_nothrow_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
+#  else
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
+#  endif
+
+  // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+  // pointers
+  union __storage_t {
+    void* __obj_ptr;
+    void const* __obj_const_ptr;
+    void (*__fn_ptr)();
+
+    constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {};
+
+    template <class _Tp>
+    constexpr explicit __storage_t(_Tp* __ptr) noexcept {
+      if constexpr (is_object_v<_Tp>) {
+        if constexpr (is_const_v<_Tp>) {
+          __obj_const_ptr = __ptr;
+        } else {
+          __obj_ptr = __ptr;
+        }
+      } else {
+        static_assert(is_function_v<_Tp>);
+        __fn_ptr = reinterpret_cast<void (*)()>(__ptr);
+      }
+    }
+  } __storage_;
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr);
+    }
+  }
+
+  using __call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+  __call_t __call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fp, class _Tp = remove_reference_t<_Fp>>
+    requires(!__is_function_ref<remove_cvref_t<_Fp>> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+          return __obj(std::forward<_ArgTypes>(__args)...);
+        }) {}
+
+  template <auto _Fn>
+    requires __is_invocable_using<decltype(_Fn)>
+  constexpr function_ref(nontype_t<_Fn>) noexcept
+      : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
+  constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Fn, class _Tp>
+    requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
+  constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+      : __storage_(__obj_ptr),
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<decltype(_Fn)>) {
+      _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  constexpr function_ref(const function_ref&) noexcept = default;
+
+  constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
+  function_ref& operator=(_Tp) = delete;
+
+  constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
+                           _Tp> {
+  using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
+                           _Tp> {
+  using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
new file mode 100644
index 0000000000000..86893f17e2d96
--- /dev/null
+++ b/libcxx/include/__utility/nontype.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_NONTYPE_H
+#define _LIBCPP___UTILITY_NONTYPE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <auto _Vp>
+struct nontype_t {
+  explicit nontype_t() = default;
+};
+
+template <auto _Vp>
+inline constexpr nontype_t<_Vp> nontype{};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_NONTYPE_H
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 27cf21e1a4c8b..f413e296fab9e 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -541,6 +541,7 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #include <__functional/compose.h>
 #include <__functional/default_searcher.h>
 #include <__functional/function.h>
+#include <__functional/function_ref.h>
 #include <__functional/hash.h>
 #include <__functional/identity.h>
 #include <__functional/invoke.h>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 48391b2a12095..5143f3fadfdc2 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1339,6 +1339,7 @@ module std_private_functional_compose                    [system] {
 }
 module std_private_functional_default_searcher           [system] { header "__functional/default_searcher.h" }
 module std_private_functional_function                   [system] { header "__functional/function.h" }
+module std_private_functional_function_ref               [system] { header "__functional/function_ref.h" }
 module std_private_functional_hash                       [system] {
   header "__functional/hash.h"
   export std_cstdint
@@ -2073,6 +2074,7 @@ module std_private_utility_move                   [system] {
   export std_private_type_traits_remove_reference
 }
 module std_private_utility_no_destroy             [system] { header "__utility/no_destroy.h" }
+module std_private_utility_nontype                [system] { header "__utility/nontype.h" }
 module std_private_utility_pair                   [system] {
   header "__utility/pair.h"
   export std_private_ranges_subrange_fwd
diff --git a/libcxx/include/utility b/libcxx/include/utility
index 90713da621c5d..07dba7593f2df 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -238,6 +238,15 @@ template <size_t I>
 template <size_t I>
   inline constexpr in_place_index_t<I> in_place_index{};
 
+// nontype argument tag
+template<auto V>
+  struct nontype_t {
+    explicit nontype_t() = default;
+  };
+template<auto V> constexpr nontype_t<V> nontype{};
+
+template<auto V> constexpr nontype_t<V> nontype{};
+
 // [utility.underlying], to_underlying
 template <class T>
     constexpr underlying_type_t<T> to_underlying( T value ) noexcept; // C++23
@@ -259,6 +268,7 @@ template <class T>
 #include <__utility/in_place.h>
 #include <__utility/integer_sequence.h>
 #include <__utility/move.h>
+#include <__utility/nontype.h>
 #include <__utility/pair.h>
 #include <__utility/piecewise_construct.h>
 #include <__utility/priority_tag.h>
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index ddc7d023ee6dc..4d2be32f3f6bb 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -91,6 +91,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/modules/std/utility.inc b/libcxx/modules/std/utility.inc
index 77c21b87640dd..28f8e36e30af9 100644
--- a/libcxx/modules/std/utility.inc
+++ b/libcxx/modules/std/utility.inc
@@ -89,6 +89,11 @@ export namespace std {
   using std::in_place_index;
   using std::in_place_index_t;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::nontype;
+  using std::nontype_t;
+#endif // _LIBCPP_STD_VER >= 23
+
   // [depr.relops]
   namespace rel_ops {
     using rel_ops::operator!=;
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
new file mode 100644
index 0000000000000..c7490fc0982e3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto f>
+  //  function_ref(nontype_t<f>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::nontype_t<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::nontype_t<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto f, class T>
+  //  function_ref(nontype_t<f>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::nontype_t<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&()>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&()>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::nontype_t<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
new file mode 100644
index 000000...
[truncated]

``````````

</details>


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


More information about the libcxx-commits mailing list