On Thu, 28 Nov 2024, Jonathan Wakely wrote:
> 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.
Might as well remove the __gnu_cxx exports in std.cc.in while we're at
it?
>
> For the operator<=> comparing two iterators of the same type, I had to
> use a deduced return type and add a requires-clause, because it's no
> longer a template and so we no longer get substitution failures when
> it's considered in oerload resolution.
>
> 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.
> * src/c++11/string-inst.cc: Remove explicit instantiations of
> operators that are no longer templates.
> ---
>
> Tested x86_64-linux.
>
> This iterator type isn't defined in the standard, and users shouldn't be
> doing funny things with it, so nothing prevents us from replacing its
> operators with hidden friends.
>
> libstdc++-v3/include/bits/stl_iterator.h | 341 ++++++++++++-----------
> libstdc++-v3/src/c++11/string-inst.cc | 11 -
> 2 files changed, 184 insertions(+), 168 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/stl_iterator.h
> b/libstdc++-v3/include/bits/stl_iterator.h
> index e872598d7d8..656a47e5f76 100644
> --- a/libstdc++-v3/include/bits/stl_iterator.h
> +++ b/libstdc++-v3/include/bits/stl_iterator.h
> @@ -1164,188 +1164,215 @@ _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()); }
> + template<typename _Iter>
> + static constexpr bool __nothrow_synth3way
> + = noexcept(std::__detail::__synth3way(std::declval<_Iterator&>(),
> + std::declval<_Iter&>()));
Since base() returns a const reference do we want to consider const
references in this noexcept helper?
>
> - 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 _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(__nothrow_synth3way<_Iter>)
> + requires requires {
> + std::__detail::__synth3way(__lhs.base(), __rhs.base());
> + }
> + { return std::__detail::__synth3way(__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()); }
> + [[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(); }
> +
> + [[nodiscard, __gnu__::__always_inline__]]
> + friend
> + constexpr auto
> + operator<=>(const __normal_iterator& __lhs,
> + const __normal_iterator& __rhs)
> + noexcept(__nothrow_synth3way<_Iterator>)
> + requires requires {
> + std::__detail::__synth3way(__lhs.base() == __rhs.base());
Copy/paste typo here? s/==/,
> + }
> + { 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
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
IIUC hidden friends are implicitly inline so we can remove the inline
keyword here.
> + operator==(const __normal_iterator& __lhs,
> + const __normal_iterator<_Iter, _Container>& __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() == __rhs.base(); }
>
> - template<typename _Iterator, typename _Container>
> - _GLIBCXX_NODISCARD __attribute__((__always_inline__)) _GLIBCXX_CONSTEXPR
> - inline bool
> - operator==(const __normal_iterator<_Iterator, _Container>& __lhs,
> - const __normal_iterator<_Iterator, _Container>& __rhs)
> - _GLIBCXX_NOEXCEPT
> - { return __lhs.base() == __rhs.base(); }
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
> + operator==(const __normal_iterator& __lhs, const __normal_iterator&
> __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() == __rhs.base(); }
>
> - 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(); }
> + template<typename _Iter>
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
> + operator!=(const __normal_iterator& __lhs,
> + const __normal_iterator<_Iter, _Container>& __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() != __rhs.base(); }
>
> - template<typename _Iterator, typename _Container>
> - _GLIBCXX_NODISCARD __attribute__((__always_inline__)) _GLIBCXX_CONSTEXPR
> - inline bool
> - operator!=(const __normal_iterator<_Iterator, _Container>& __lhs,
> - const __normal_iterator<_Iterator, _Container>& __rhs)
> - _GLIBCXX_NOEXCEPT
> - { return __lhs.base() != __rhs.base(); }
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
> + operator!=(const __normal_iterator& __lhs, const __normal_iterator&
> __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() != __rhs.base(); }
>
> - // Random access 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(); }
> + // Random access iterator requirements
> + template<typename _Iter>
> + friend
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD _GLIBCXX_CONSTEXPR
> + inline bool
> + operator<(const __normal_iterator& __lhs,
> + const __normal_iterator<_Iter, _Container>& __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() < __rhs.base(); }
>
> - template<typename _Iterator, typename _Container>
> - _GLIBCXX_NODISCARD __attribute__((__always_inline__))
> _GLIBCXX20_CONSTEXPR
> - inline bool
> - operator<(const __normal_iterator<_Iterator, _Container>& __lhs,
> - const __normal_iterator<_Iterator, _Container>& __rhs)
> - _GLIBCXX_NOEXCEPT
> - { return __lhs.base() < __rhs.base(); }
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX20_CONSTEXPR
> + inline bool
> + operator<(const __normal_iterator& __lhs, const __normal_iterator&
> __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() < __rhs.base(); }
>
> - 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(); }
> + template<typename _Iter>
> + friend
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD _GLIBCXX_CONSTEXPR
> + inline bool
> + operator>(const __normal_iterator& __lhs,
> + const __normal_iterator<_Iter, _Container>& __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() > __rhs.base(); }
>
> - template<typename _Iterator, typename _Container>
> - _GLIBCXX_NODISCARD __attribute__((__always_inline__)) _GLIBCXX_CONSTEXPR
> - inline bool
> - operator>(const __normal_iterator<_Iterator, _Container>& __lhs,
> - const __normal_iterator<_Iterator, _Container>& __rhs)
> - _GLIBCXX_NOEXCEPT
> - { return __lhs.base() > __rhs.base(); }
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
> + operator>(const __normal_iterator& __lhs, const __normal_iterator&
> __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() > __rhs.base(); }
>
> - 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(); }
> + template<typename _Iter>
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
> + operator<=(const __normal_iterator& __lhs,
> + const __normal_iterator<_Iter, _Container>& __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() <= __rhs.base(); }
>
> - template<typename _Iterator, typename _Container>
> - _GLIBCXX_NODISCARD __attribute__((__always_inline__)) _GLIBCXX_CONSTEXPR
> - inline bool
> - operator<=(const __normal_iterator<_Iterator, _Container>& __lhs,
> - const __normal_iterator<_Iterator, _Container>& __rhs)
> - _GLIBCXX_NOEXCEPT
> - { return __lhs.base() <= __rhs.base(); }
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
> + operator<=(const __normal_iterator& __lhs, const __normal_iterator&
> __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() <= __rhs.base(); }
>
> - 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(); }
> + template<typename _Iter>
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
> + operator>=(const __normal_iterator& __lhs,
> + const __normal_iterator<_Iter, _Container>& __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() >= __rhs.base(); }
>
> - template<typename _Iterator, typename _Container>
> - _GLIBCXX_NODISCARD __attribute__((__always_inline__)) _GLIBCXX_CONSTEXPR
> - inline bool
> - operator>=(const __normal_iterator<_Iterator, _Container>& __lhs,
> - const __normal_iterator<_Iterator, _Container>& __rhs)
> - _GLIBCXX_NOEXCEPT
> - { return __lhs.base() >= __rhs.base(); }
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline bool
> + operator>=(const __normal_iterator& __lhs, const __normal_iterator&
> __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() >= __rhs.base(); }
> #endif // three-way comparison
>
> - // _GLIBCXX_RESOLVE_LIB_DEFECTS
> - // According to the resolution of DR179 not only the various comparison
> - // operators but also operator- must accept mixed iterator/const_iterator
> - // parameters.
> - template<typename _IteratorL, typename _IteratorR, typename _Container>
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 179. Comparison of const_iterators to iterators doesn't work
> + // According to the resolution of DR179 not only the various comparison
> + // operators but also operator- must accept mixed
> iterator/const_iterator
> + // parameters.
> + template<typename _Iter>
> #if __cplusplus >= 201103L
> - // DR 685.
> - [[__nodiscard__, __gnu__::__always_inline__]]
> - constexpr auto
> - operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
> - const __normal_iterator<_IteratorR, _Container>& __rhs) noexcept
> - -> decltype(__lhs.base() - __rhs.base())
> + [[__nodiscard__, __gnu__::__always_inline__]]
> + friend
> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> + // 685. reverse_iterator/move_iterator difference has invalid signatures
> + constexpr auto
> + operator-(const __normal_iterator& __lhs,
> + const __normal_iterator<_Iter, _Container>& __rhs) noexcept
> + -> decltype(__lhs.base() - __rhs.base())
> #else
> - inline typename __normal_iterator<_IteratorL,
> _Container>::difference_type
> - operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
> - const __normal_iterator<_IteratorR, _Container>& __rhs)
> + friend
> + difference_type
> + operator-(const __normal_iterator& __lhs,
> + const __normal_iterator<_Iter, _Container>& __rhs)
> #endif
> - { return __lhs.base() - __rhs.base(); }
> + { return __lhs.base() - __rhs.base(); }
>
> - template<typename _Iterator, typename _Container>
> - _GLIBCXX_NODISCARD __attribute__((__always_inline__)) _GLIBCXX_CONSTEXPR
> - inline typename __normal_iterator<_Iterator, _Container>::difference_type
> - operator-(const __normal_iterator<_Iterator, _Container>& __lhs,
> - const __normal_iterator<_Iterator, _Container>& __rhs)
> - _GLIBCXX_NOEXCEPT
> - { return __lhs.base() - __rhs.base(); }
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline difference_type
> + operator-(const __normal_iterator& __lhs, const __normal_iterator&
> __rhs)
> + _GLIBCXX_NOEXCEPT
> + { return __lhs.base() - __rhs.base(); }
>
> - template<typename _Iterator, typename _Container>
> - _GLIBCXX_NODISCARD __attribute__((__always_inline__)) _GLIBCXX_CONSTEXPR
> - inline __normal_iterator<_Iterator, _Container>
> - operator+(typename __normal_iterator<_Iterator,
> _Container>::difference_type
> - __n, const __normal_iterator<_Iterator, _Container>& __i)
> - _GLIBCXX_NOEXCEPT
> - { return __normal_iterator<_Iterator, _Container>(__i.base() + __n); }
> + __attribute__((__always_inline__)) _GLIBCXX_NODISCARD
> + friend
> + _GLIBCXX_CONSTEXPR
> + inline __normal_iterator
> + operator+(difference_type __n, const __normal_iterator& __i)
> + _GLIBCXX_NOEXCEPT
> + { return __normal_iterator(__i.base() + __n); }
> + };
>
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace __gnu_cxx
> diff --git a/libstdc++-v3/src/c++11/string-inst.cc
> b/libstdc++-v3/src/c++11/string-inst.cc
> index ec5ba41ebcd..b8806304949 100644
> --- a/libstdc++-v3/src/c++11/string-inst.cc
> +++ b/libstdc++-v3/src/c++11/string-inst.cc
> @@ -110,14 +110,3 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace
> -
> -namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
> -{
> -_GLIBCXX_BEGIN_NAMESPACE_VERSION
> -
> - using std::S;
> - template bool operator==(const S::iterator&, const S::iterator&);
> - template bool operator==(const S::const_iterator&, const
> S::const_iterator&);
> -
> -_GLIBCXX_END_NAMESPACE_VERSION
> -} // namespace
> --
> 2.47.0
>
>