[libcxx-commits] [libcxx] [libc++] Implement `std::function_ref` (PR #186692)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun May 17 06:08:10 PDT 2026


================
@@ -0,0 +1,190 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.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_reference.h>
+#include <__type_traits/is_same.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/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
+    : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
+private:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn2>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __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, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
----------------
huixie90 wrote:

libstdc++ did this purely because they want to make some dangling case well defined.
https://github.com/gcc-mirror/gcc/commit/b58cf254437aa78046666b7444a23e2658396f80

I think this is open for debate, whether or not we want to make the UB code well formed. as you mentioned, the performance difference is probably negligible (to be confirmed). If the benchmark result does not show any difference, I would argue if the added complexity is worth the effort. 

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


More information about the libcxx-commits mailing list