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