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
──────────────────────────────────────────────────────────────────────────