On Thu, Feb 5, 2026 at 8:51β―AM Matthias Kretz <[email protected]> wrote:
> 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
Yes, see: https://gcc.gnu.org/pipermail/libstdc++/2025-November/064444.html
>
>
> Maybe after adding meta::display_string_of(^^Fd).starts_with("std::") π€ͺ
>
I will make every stateless operator post-c++20 static, and already did so
for C++20 functors wrappers:
https://gcc.gnu.org/pipermail/libstdc++/2025-November/064443.html
>
> - 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
>
>
>
>