On Thu, 20 Nov 2025, Tomasz Kamiński wrote:

> The operator() for function objects introduced in C++20 (e.g., std::identity,
> std::compare_three_way, std::ranges::equal) is now defined as static.
> Although static operator() is a C++23 feature, it is supported in C++20 by
> both GCC and clang (since their support was added in clang-16).
> 
> Consequently, these types no longer require special handling in the internal
> __is_std_op_wrapper.
> 
> This change is not user-observable, as all affected operators are template
> functions. Taking the address of such an operator requires casting to a 
> pointer
> to member function with a specific signature. The exact signature is 
> unspecified
> per C++20 [member.functions] p2 (e.g., due to potential parameters with 
> default arguments).
> 
> libstdc++-v3/ChangeLog:
> 
>       * include/bits/ranges_cmp.h (std::identity::operator()):
>       (ranges::equal_to:operator(), ranges::not_equal_to:operator())
>       (ranges::greater::operator(), ranges::greater_equal::operator())
>       (ranges::less::operator(), ranges::less_equal::operator()):
>       Declare as static.
>       * include/std/ranges (std::__is_std_op_wrapper): Remove special
>       handling for affected operators.
>       * libsupc++/compare (std::compare_three_way::operator()):
>       Declare as static.

LGTM

> ---
> Instead of special casing the set of C++20 functors for transform view,
> we make their operator static, as this is supported as extensions both
> by GCC and clang.
> 
> Tested on x86_64-linux in C++20,C++23 and C++26 mode.
> 
>  libstdc++-v3/include/bits/ranges_cmp.h | 34 +++++++++++++++-----------
>  libstdc++-v3/include/std/ranges        | 10 +-------
>  libstdc++-v3/libsupc++/compare         |  7 ++++--
>  3 files changed, 26 insertions(+), 25 deletions(-)
> 
> diff --git a/libstdc++-v3/include/bits/ranges_cmp.h 
> b/libstdc++-v3/include/bits/ranges_cmp.h
> index cd5f7b8b37a..a53cd564563 100644
> --- a/libstdc++-v3/include/bits/ranges_cmp.h
> +++ b/libstdc++-v3/include/bits/ranges_cmp.h
> @@ -46,11 +46,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    /// [func.identity] The identity function.
>    struct identity
>    {
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wc++23-extensions" // static operator()
>      template<typename _Tp>
>        [[nodiscard]]
> -      constexpr _Tp&&
> -      operator()(_Tp&& __t) const noexcept
> +      static constexpr _Tp&&
> +      operator()(_Tp&& __t) noexcept
>        { return std::forward<_Tp>(__t); }
> +#pragma GCC diagnostic pop
>  
>      using is_transparent = __is_transparent;
>    };
> @@ -79,13 +82,15 @@ namespace ranges
>    // _GLIBCXX_RESOLVE_LIB_DEFECTS
>    // 3530 BUILTIN-PTR-MEOW should not opt the type out of syntactic checks
>  
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wc++23-extensions" // static operator()
>    /// ranges::equal_to function object type.
>    struct equal_to
>    {
>      template<typename _Tp, typename _Up>
>        requires equality_comparable_with<_Tp, _Up>
> -      constexpr bool
> -      operator()(_Tp&& __t, _Up&& __u) const
> +      static constexpr bool
> +      operator()(_Tp&& __t, _Up&& __u)
>        noexcept(noexcept(std::declval<_Tp>() == std::declval<_Up>()))
>        { return std::forward<_Tp>(__t) == std::forward<_Up>(__u); }
>  
> @@ -97,8 +102,8 @@ namespace ranges
>    {
>      template<typename _Tp, typename _Up>
>        requires equality_comparable_with<_Tp, _Up>
> -      constexpr bool
> -      operator()(_Tp&& __t, _Up&& __u) const
> +      static constexpr bool
> +      operator()(_Tp&& __t, _Up&& __u)
>        noexcept(noexcept(std::declval<_Tp>() == std::declval<_Up>()))
>        { return !equal_to{}(std::forward<_Tp>(__t), std::forward<_Up>(__u)); }
>  
> @@ -110,8 +115,8 @@ namespace ranges
>    {
>      template<typename _Tp, typename _Up>
>        requires totally_ordered_with<_Tp, _Up>
> -      constexpr bool
> -      operator()(_Tp&& __t, _Up&& __u) const
> +      static constexpr bool
> +      operator()(_Tp&& __t, _Up&& __u)
>        noexcept(noexcept(std::declval<_Tp>() < std::declval<_Up>()))
>        {
>       if constexpr (__detail::__less_builtin_ptr_cmp<_Tp, _Up>)
> @@ -137,8 +142,8 @@ namespace ranges
>    {
>      template<typename _Tp, typename _Up>
>        requires totally_ordered_with<_Tp, _Up>
> -      constexpr bool
> -      operator()(_Tp&& __t, _Up&& __u) const
> +      static constexpr bool
> +      operator()(_Tp&& __t, _Up&& __u)
>        noexcept(noexcept(std::declval<_Up>() < std::declval<_Tp>()))
>        { return less{}(std::forward<_Up>(__u), std::forward<_Tp>(__t)); }
>  
> @@ -150,8 +155,8 @@ namespace ranges
>    {
>      template<typename _Tp, typename _Up>
>        requires totally_ordered_with<_Tp, _Up>
> -      constexpr bool
> -      operator()(_Tp&& __t, _Up&& __u) const
> +      static constexpr bool
> +      operator()(_Tp&& __t, _Up&& __u)
>        noexcept(noexcept(std::declval<_Tp>() < std::declval<_Up>()))
>        { return !less{}(std::forward<_Tp>(__t), std::forward<_Up>(__u)); }
>  
> @@ -163,13 +168,14 @@ namespace ranges
>    {
>      template<typename _Tp, typename _Up>
>        requires totally_ordered_with<_Tp, _Up>
> -      constexpr bool
> -      operator()(_Tp&& __t, _Up&& __u) const
> +      static constexpr bool
> +      operator()(_Tp&& __t, _Up&& __u)
>        noexcept(noexcept(std::declval<_Up>() < std::declval<_Tp>()))
>        { return !less{}(std::forward<_Up>(__u), std::forward<_Tp>(__t)); }
>  
>      using is_transparent = __is_transparent;
>    };
> +#pragma GCC diagnostic pop
>  
>  } // namespace ranges
>  #endif // __glibcxx_ranges
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index b384e5e45d3..f459b89823d 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -297,20 +297,12 @@ namespace ranges
>  
>        template<typename _Fn>
>       concept __is_std_op_wrapper = is_empty_v<_Fn> && (
> -       is_same_v<_Fn, std::identity>
> -       || __is_specialization_of<_Fn, std::equal_to>
> +       __is_specialization_of<_Fn, std::equal_to>
>         || __is_specialization_of<_Fn, std::not_equal_to>
>         || __is_specialization_of<_Fn, std::greater>
>         || __is_specialization_of<_Fn, std::less>
>         || __is_specialization_of<_Fn, std::greater_equal>
>         || __is_specialization_of<_Fn, std::less_equal>
> -       || is_same_v<_Fn, std::compare_three_way>
> -       || is_same_v<_Fn, std::ranges::equal_to>
> -       || is_same_v<_Fn, std::ranges::not_equal_to>
> -       || is_same_v<_Fn, std::ranges::greater>
> -       || is_same_v<_Fn, std::ranges::less>
> -       || is_same_v<_Fn, std::ranges::greater_equal>
> -       || is_same_v<_Fn, std::ranges::less_equal>
>         || __is_specialization_of<_Fn, std::plus>
>         || __is_specialization_of<_Fn, std::minus>
>         || __is_specialization_of<_Fn, std::multiplies>
> diff --git a/libstdc++-v3/libsupc++/compare b/libstdc++-v3/libsupc++/compare
> index 458b47c3fca..08f2b2ba47e 100644
> --- a/libstdc++-v3/libsupc++/compare
> +++ b/libstdc++-v3/libsupc++/compare
> @@ -573,10 +573,12 @@ namespace std _GLIBCXX_VISIBILITY(default)
>    // [cmp.object], typename compare_three_way
>    struct compare_three_way
>    {
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wc++23-extensions" // static operator()
>      template<typename _Tp, typename _Up>
>        requires three_way_comparable_with<_Tp, _Up>
> -      constexpr auto
> -      operator() [[nodiscard]] (_Tp&& __t, _Up&& __u) const
> +      static constexpr auto
> +      operator() [[nodiscard]] (_Tp&& __t, _Up&& __u)
>        noexcept(noexcept(std::declval<_Tp>() <=> std::declval<_Up>()))
>        {
>       if constexpr (__detail::__3way_builtin_ptr_cmp<_Tp, _Up>)
> @@ -592,6 +594,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
>       else
>         return static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u);
>        }
> +#pragma GCC diagnostic pop
>  
>      using is_transparent = void;
>    };
> -- 
> 2.51.1
> 
> 

Reply via email to