Am Do., 12. Juni 2025 um 00:10 Uhr schrieb Jonathan Wakely <
jwak...@redhat.com>:

> As suggested by Jason, this makes all __normal_iterator operators into
> friends so they can be found by ADL and don't need to be separately
> exported in module std.
>
> The operator<=> comparing two iterators of the same type is removed
> entirely, instead of being made a hidden friend. That overload was added
> by r12-5882-g2c7fb16b5283cf to deal with unconstrained operator
> overloads found by ADL, as defined in the testsuite_greedy_ops.h header.
> We don't actually test that case as there's no unconstrained <=> in that
> header, and it doesn't seem reasonable for anybody to define such an
> operator<=> in C++20 when they should constrain their overloads properly
> (e.g. using a requires-clause). The heterogeneous operator<=> overloads
> added for reverse_iterator and move_iterator could also be removed, but
> that's not part of this commit.
>
> I also had to reorder the __attribute__((always_inline)) and
> [[nodiscard]] attributes, which have to be in a particular order when
> used on friend functions.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/stl_iterator.h (__normal_iterator): Make all
>         non-member operators hidden friends, except ...
>         (operator<=>(__normal_iterator<I,C>, __normal_iterator<I,C>)):
>         Remove.
>         * src/c++11/string-inst.cc: Remove explicit instantiations of
>         operators that are no longer templates.
>         * src/c++23/std.cc.in (__gnu_cxx): Do not export operators for
>         __normal_iterator.
> ---
>
> v2: removed the unnecessary operator<=>, removed std.cc exports, fixed
> other minor issues noticed by Patrick.
>
> Tested x86_64-linux.
>
>  libstdc++-v3/include/bits/stl_iterator.h | 327 ++++++++++++-----------
>  libstdc++-v3/src/c++11/string-inst.cc    |  11 -
>  libstdc++-v3/src/c++23/std.cc.in         |   9 -
>  3 files changed, 169 insertions(+), 178 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/stl_iterator.h
> b/libstdc++-v3/include/bits/stl_iterator.h
> index 478a98fe8a4f..a7188f46f6db 100644
> --- a/libstdc++-v3/include/bits/stl_iterator.h
> +++ b/libstdc++-v3/include/bits/stl_iterator.h
> @@ -1164,188 +1164,199 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        const _Iterator&
>        base() const _GLIBCXX_NOEXCEPT
>        { return _M_current; }
> -    };
>
> -  // Note: In what follows, the left- and right-hand-side iterators are
> -  // allowed to vary in types (conceptually in cv-qualification) so that
> -  // comparison between cv-qualified and non-cv-qualified iterators be
> -  // valid.  However, the greedy and unfriendly operators in std::rel_ops
> -  // will make overload resolution ambiguous (when in scope) if we don't
> -  // provide overloads whose operands are of the same type.  Can someone
> -  // remind me what generic programming is about? -- Gaby
> +    private:
> +      // Note: In what follows, the left- and right-hand-side iterators
> are
> +      // allowed to vary in types (conceptually in cv-qualification) so
> that
> +      // comparison between cv-qualified and non-cv-qualified iterators be
> +      // valid.  However, the greedy and unfriendly operators in
> std::rel_ops
> +      // will make overload resolution ambiguous (when in scope) if we
> don't
> +      // provide overloads whose operands are of the same type.  Can
> someone
> +      // remind me what generic programming is about? -- Gaby
>
>  #ifdef __cpp_lib_three_way_comparison
> -  template<typename _IteratorL, typename _IteratorR, typename _Container>
> -    [[nodiscard, __gnu__::__always_inline__]]
> -    constexpr bool
> -    operator==(const __normal_iterator<_IteratorL, _Container>& __lhs,
> -              const __normal_iterator<_IteratorR, _Container>& __rhs)
> -    noexcept(noexcept(__lhs.base() == __rhs.base()))
> -    requires requires {
> -      { __lhs.base() == __rhs.base() } -> std::convertible_to<bool>;
> -    }
> -    { return __lhs.base() == __rhs.base(); }
> +      template<typename _Iter>
> +       [[nodiscard, __gnu__::__always_inline__]]
> +       friend
> +       constexpr bool
> +       operator==(const __normal_iterator& __lhs,
> +                  const __normal_iterator<_Iter, _Container>& __rhs)
> +       noexcept(noexcept(__lhs.base() == __rhs.base()))
> +       requires requires {
> +         { __lhs.base() == __rhs.base() } -> std::convertible_to<bool>;
> +       }
> +       { return __lhs.base() == __rhs.base(); }
>
> -  template<typename _IteratorL, typename _IteratorR, typename _Container>
> -    [[nodiscard, __gnu__::__always_inline__]]
> -    constexpr std::__detail::__synth3way_t<_IteratorR, _IteratorL>
> -    operator<=>(const __normal_iterator<_IteratorL, _Container>& __lhs,
> -               const __normal_iterator<_IteratorR, _Container>& __rhs)
> -    noexcept(noexcept(std::__detail::__synth3way(__lhs.base(),
> __rhs.base())))
> -    { return std::__detail::__synth3way(__lhs.base(), __rhs.base()); }
> +      [[nodiscard, __gnu__::__always_inline__]]
> +      friend
> +      constexpr bool
> +      operator==(const __normal_iterator& __lhs, const __normal_iterator&
> __rhs)
> +      noexcept(noexcept(__lhs.base() == __rhs.base()))
> +      requires requires {
> +       { __lhs.base() == __rhs.base() } -> std::convertible_to<bool>;
> +      }
> +      { return __lhs.base() == __rhs.base(); }
>
> -  template<typename _Iterator, typename _Container>
> -    [[nodiscard, __gnu__::__always_inline__]]
> -    constexpr bool
> -    operator==(const __normal_iterator<_Iterator, _Container>& __lhs,
> -              const __normal_iterator<_Iterator, _Container>& __rhs)
> -    noexcept(noexcept(__lhs.base() == __rhs.base()))
> -    requires requires {
> -      { __lhs.base() == __rhs.base() } -> std::convertible_to<bool>;
> -    }
> -    { return __lhs.base() == __rhs.base(); }
> -
> -  template<typename _Iterator, typename _Container>
> -    [[nodiscard, __gnu__::__always_inline__]]
> -    constexpr std::__detail::__synth3way_t<_Iterator>
> -    operator<=>(const __normal_iterator<_Iterator, _Container>& __lhs,
> -               const __normal_iterator<_Iterator, _Container>& __rhs)
> -    noexcept(noexcept(std::__detail::__synth3way(__lhs.base(),
> __rhs.base())))
> -    { return std::__detail::__synth3way(__lhs.base(), __rhs.base()); }
> +      template<typename _Iter>
> +       [[nodiscard, __gnu__::__always_inline__]]
> +       friend
> +       constexpr std::__detail::__synth3way_t<_Iterator, _Iter>
> +       operator<=>(const __normal_iterator& __lhs,
> +                   const __normal_iterator<_Iter, _Container>& __rhs)
> +       noexcept(noexcept(std::__detail::__synth3way(__lhs.base(),
> __rhs.base())))
> +       requires requires {
> +         std::__detail::__synth3way(__lhs.base(), __rhs.base());
> +       }
> +       { return std::__detail::__synth3way(__lhs.base(), __rhs.base()); }
>  #else
> -   // Forward iterator requirements
> -  template<typename _IteratorL, typename _IteratorR, typename _Container>
> -    _GLIBCXX_NODISCARD __attribute__((__always_inline__))
> _GLIBCXX_CONSTEXPR
> -    inline bool
> -    operator==(const __normal_iterator<_IteratorL, _Container>& __lhs,
> -              const __normal_iterator<_IteratorR, _Container>& __rhs)
> -    _GLIBCXX_NOEXCEPT
> -    { return __lhs.base() == __rhs.base(); }
> +       // Forward iterator requirements
> +      template<typename _Iter>
> +       __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
>

Just curious: So in this case the attribute order intentionally potentially
deviates from the order of the other friend functions using   [[nodiscard,
__gnu__::__always_inline__]]?

- Daniel

Reply via email to