FWIW, this all makes sense to me and the implementation looks good.

One question though:

Tomasz Kamiński [Monday, 2 February 2026 11:01:10 CET]:
> diff --git a/libstdc++-v3/include/bits/funcref_impl.h
> b/libstdc++-v3/include/bits/funcref_impl.h index 3fe2fb1b3f5..3d55c8406b0
> 100644
> --- a/libstdc++-v3/include/bits/funcref_impl.h
> +++ b/libstdc++-v3/include/bits/funcref_impl.h
> @@ -106,8 +106,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       constexpr
>       function_ref(_Fn&& __f) noexcept
>       {
> -       _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>();
> -       _M_init(std::addressof(__f));
> +       using _Fd = remove_cv_t<_Vt>;
> +       if constexpr (__is_std_op_wrapper<_Fd>)

can we add (or replace the condition with):

|| (is_empty_v<_Fd> && requires { typename constant_wrapper<_Fd{}>; })

- Matthias

> +         {
> +           _M_invoke = _Invoker::template _S_nttp<_Fd{}>;
> +           _M_ptrs._M_obj = nullptr;
> +         }
> +       else if constexpr (requires (_ArgTypes&&... __args) {
> +                 _Fd::operator()(std::forward<_ArgTypes>(__args)...);
> +               })
> +         {
> +           _M_invoke = _Invoker::template _S_static<_Fd>;
> +           _M_ptrs._M_obj = nullptr;
> +         }
> +          else
> +         {
> +           _M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>();
> +           _M_init(std::addressof(__f));
> +         }
>       }
> 
>        // _GLIBCXX_RESOLVE_LIB_DEFECTS
> diff --git a/libstdc++-v3/include/bits/funcwrap.h
> b/libstdc++-v3/include/bits/funcwrap.h index b8dd6fb7aea..6441893d213
> 100644
> --- a/libstdc++-v3/include/bits/funcwrap.h
> +++ b/libstdc++-v3/include/bits/funcwrap.h
> @@ -41,6 +41,9 @@
> 
>  #include <bits/invoke.h>
>  #include <bits/utility.h>
> +#if __glibcxx_function_ref
> +#include <bits/stl_function.h>
> +#endif
> 
>  namespace std _GLIBCXX_VISIBILITY(default)
>  {
> @@ -139,6 +142,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        { return &_S_call_ptrs<_Adjust_target<_Tp>>; }
> 
>  #ifdef __glibcxx_function_ref // C++ >= 26
> +       template<typename _Fn>
> +      static _Ret
> +      _S_static(_Ptrs, _Args... __args) noexcept(_Noex)
> +      { return _Fn::operator()(std::forward<_Args>(__args)...); }
> +
>         template<auto __fn>
>        static _Ret
>        _S_nttp(_Ptrs, _Args... __args) noexcept(_Noex)
> diff --git a/libstdc++-v3/include/bits/stl_function.h
> b/libstdc++-v3/include/bits/stl_function.h index 0600de72b10..6609fe3199b
> 100644
> --- a/libstdc++-v3/include/bits/stl_function.h
> +++ b/libstdc++-v3/include/bits/stl_function.h
> @@ -1525,6 +1525,56 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  #endif
>  #endif
> 
> +#if __cplusplus > 201703L
> +  template<template<typename> class>
> +    constexpr bool __is_std_op_template = false;
> +
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::equal_to> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::not_equal_to> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::greater> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::less> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::greater_equal> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::less_equal> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::plus> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::minus> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::multiplies> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::divides> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::modulus> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::negate> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::logical_and> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::logical_or> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::logical_not> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::bit_and> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::bit_or> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::bit_xor> = true;
> +  template<>
> +    inline constexpr bool __is_std_op_template<std::bit_not> = true;
> +
> +  template<typename _Fn>
> +    constexpr bool __is_std_op_wrapper = false;
> +
> +  template<template<typename> class _Ft, typename _Tp>
> +    constexpr bool __is_std_op_wrapper<_Ft<_Tp>>
> +      = __is_std_op_template<_Ft>;
> +#endif
>  _GLIBCXX_END_NAMESPACE_VERSION
>  } // namespace
> 
> diff --git a/libstdc++-v3/include/std/ranges
> b/libstdc++-v3/include/std/ranges index 430f4108204..8365bed17a6 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -287,56 +287,6 @@ namespace ranges
>       { return std::__addressof(_M_value); }
>        };
> 
> -      template<template<typename> class>
> -     constexpr bool __is_std_op_template = false;
> -
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::equal_to> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::not_equal_to> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::greater> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::less> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::greater_equal> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::less_equal> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::plus> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::minus> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::multiplies> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::divides> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::modulus> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::negate> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::logical_and> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::logical_or> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::logical_not> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::bit_and> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::bit_or> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::bit_xor> = true;
> -      template<>
> -     inline constexpr bool __is_std_op_template<std::bit_not> = true;
> -
> -
> -      template<typename _Fn>
> -     constexpr bool __is_std_op_wrapper = false;
> -
> -      template<template<typename> class _Ft, typename _Tp>
> -     constexpr bool __is_std_op_wrapper<_Ft<_Tp>>
> -       = __is_std_op_template<_Ft>;
> -
>        namespace __func_handle
>        {
>       template<typename _Fn>
> diff --git a/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc new file mode
> 100644
> index 00000000000..3cc782524f6
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/function_ref/dangling.cc
> @@ -0,0 +1,74 @@
> +// { dg-do compile { target c++26 } }
> +
> +#include <functional>
> +
> +template<typename F>
> +constexpr std::function_ref<int(int, int) const>
> +create(F f)
> +{
> +  std::function_ref<int(int, int) const> fr(f);
> +  return fr;
> +}
> +
> +constexpr auto vLambda = create([](int x, int y) static { return x + y; });
> +
> +constexpr auto vTgreater = create(std::greater<int>{});
> +constexpr auto vDgreater = create(std::greater<>{});
> +constexpr auto vRgreater = create(std::ranges::greater{});
> +
> +constexpr auto vTplus = create(std::plus<int>{});
> +constexpr auto vDplus = create(std::plus<>{});
> +
> +struct Empty
> +{
> +  static int
> +  operator()(int x, int y)
> +  { return x + y; }
> +};
> +
> +constexpr auto vEmpty = create(Empty{});
> +
> +struct NonEmpty {
> +  int v;
> +
> +  static int
> +  operator()(int x, int y)
> +  { return x + y; }
> +};
> +
> +constexpr auto vNonEmpty = create(NonEmpty{3});
> +
> +struct NonStatic {
> +  int v;
> +
> +  int
> +  operator()(int x, int y) const
> +  { return x + y + v; }
> +};
> +
> +constexpr auto vNonType = create(std::nontype<NonStatic{3}>);
> +
> +struct StaticWins {
> +  static int
> +  operator()(int x, int y)
> +  { return x + y; }
> +
> +  int
> +  operator()(float x, float y) const
> +  { return x + y; }
> +};
> +
> +constexpr auto vStaticWins = create(StaticWins{});
> +
> +struct StaticWinsET {
> +  static int
> +  operator()(int x, int y)
> +  { return x + y; }
> +
> +  int
> +  operator()(this StaticWinsET, float x, float y)
> +  { return x + y; }
> +};
> +
> +constexpr auto vStaticWinsET = create(StaticWinsET{});
> +
> diff --git a/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
> b/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc new file mode
> 100644
> index 00000000000..16033014703
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/function_ref/dangling_neg.cc
> @@ -0,0 +1,76 @@
> +// { dg-do compile { target c++26 } }
> +
> +#include <functional>
> +
> +template<typename F>
> +constexpr std::function_ref<int(int, int) const>
> +create(F f)
> +{
> +  std::function_ref<int(int, int) const> fr(f);
> +  return fr;
> +}
> +
> +constexpr auto vLambda = create([](int x, int y) { return x + y; }); // {
> dg-error "is not a constant expression" } +
> +struct Empty
> +{
> +  int
> +  operator()(int x, int y) const
> +  { return x + y; }
> +};
> +
> +constexpr auto vEmpty = create(Empty{}); // { dg-error "is not a constant
> expression" } +
> +struct NonEmpty {
> +  int v;
> +
> +  int
> +  operator()(int x, int y) const
> +  { return x + y + v; }
> +};
> +
> +constexpr auto vNonEmpty = create(NonEmpty{3}); // { dg-error "is not a
> constant expression" } +
> +struct InstanceWins {
> +  int
> +  operator()(int x, int y) const
> +  { return x + y; }
> +
> +  static int
> +  operator()(float x, float y)
> +  { return x + y; }
> +};
> +
> +constexpr auto vInstanceWins = create(InstanceWins{}); // { dg-error "is
> not a constant expression" } +
> +struct EmptyET
> +{
> +  int
> +  operator()(this EmptyET, int x, int y)
> +  { return x + y; }
> +};
> +
> +constexpr auto vEmptyET = create(EmptyET{}); // { dg-error "is not a
> constant expression" } +
> +struct NonEmptyET {
> +  int v;
> +
> +  int
> +  operator()(this NonEmptyET s, int x, int y)
> +  { return x + y + s.v; }
> +};
> +
> +constexpr auto vNonEmptyET = create(NonEmptyET{3}); // { dg-error "is not a
> constant expression" } +
> +struct InstanceWinsET {
> +  int
> +  operator()(this InstanceWinsET, int x, int y)
> +  { return x + y; }
> +
> +  static int
> +  operator()(float x, float y)
> +  { return x + y; }
> +};
> +
> +constexpr auto vInstanceWinsET = create(InstanceWinsET{}); // { dg-error
> "is not a constant expression" } +


-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Center for Heavy Ion Research               https://gsi.de
 std::simd
──────────────────────────────────────────────────────────────────────────



Reply via email to