Matthias Kretz [Thursday, 5 February 2026 08:30:12 CET]:
> 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{}>; })

Hmm, maybe not. The this pointer to non-static operator() is still something 
the user could be abusing:

https://compiler-explorer.com/z/T3n7hE6EG

Maybe after adding meta::display_string_of(^^Fd).starts_with("std::") πŸ€ͺ

- 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           β”‚ SDE β€” Software Development for Experiments
 Senior Software Engineer,    β”‚ πŸ“ž +49 6159 713084
 SIMD Expert,                 β”‚ πŸ“§ [email protected]     floss.social/@mkretz
 ISO C++ Numerics Chair       β”‚ πŸ”— mattkretz.github.io
──────────────────────────────┴────────────────────────────────────────────

GSI Helmholtzzentrum fΓΌr Schwerionenforschung GmbH
Planckstraße 1, 64291 Darmstadt, Germany, www.gsi.de

Commercial Register / Handelsregister: Amtsgericht Darmstadt, HRB 1528
Managing Directors / GeschΓ€ftsfΓΌhrung:
Prof. Dr. Thomas Nilsson, Dr. Katharina Stummeyer, JΓΆrg Blaurock
Chairman of the GSI Supervisory Board / Vorsitzender des GSI-Aufsichtsrats:
Ministerialdirigent Dr. Volkmar Dietz



Reply via email to