In libstdc++3, in the header bits/stl_iterator.h (GCC 10 source here), every binary operator for __normal_iterator has two overloads defined (here is == for example):
template<typename _IteratorL, typename _IteratorR, typename _Container>
_GLIBCXX20_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 _Iterator, typename _Container>
_GLIBCXX20_CONSTEXPR
inline bool
operator==(const __normal_iterator<_Iterator, _Container>& __lhs,
const __normal_iterator<_Iterator, _Container>& __rhs)
_GLIBCXX_NOEXCEPT
{ return __lhs.base() == __rhs.base(); }
Where .base() returns a pointer to an array element in this case. This is done throughout the library for other iterator types as well. According to comments and changelogs scattered throughout, it's done to support interoperability between iterators and const_iterators.
My question is, why are both the the <_IteratorL, _IteratorR, _Container> and <_Iterator, _Container> overloads defined for all of them? That is, why is <_Iterator, _Container> necessary? Wouldn't the former cover every case? What would break if the latter was removed?
GCC's libstdc++ implementation has a lot of street cred, so I'm sure there's a good, possibly subtle reason, but I can't figure out what it could be.
I ask because I'm currently working out some kinks in my own custom iterator implementations and looking at the STL as a model.