https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114316
--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> --- For a debug mode _Safe_iterator pair __valid_range uses this overload: template<typename _Iterator, typename _Sequence, typename _Category> inline bool __valid_range(const _Safe_iterator<_Iterator, _Sequence, _Category>& __first, const _Safe_iterator<_Iterator, _Sequence, _Category>& __last) { typename _Distance_traits<_Iterator>::__type __dist; return __first._M_valid_range(__last, __dist); } Which calls: template<typename _Iterator, typename _Sequence, typename _Category> bool _Safe_iterator<_Iterator, _Sequence, _Category>:: _M_valid_range(const _Safe_iterator& __rhs, std::pair<difference_type, _Distance_precision>& __dist, bool __check_dereferenceable) const { if (_M_singular() || __rhs._M_singular() || !_M_can_compare(__rhs)) return false; So it doesn't consider whether the range is empty. For non-debug mode iterators we have: template<typename _InputIterator> _GLIBCXX_CONSTEXPR inline bool __valid_range_aux(_InputIterator __first, _InputIterator __last, std::input_iterator_tag) { return __first == __last || (!__gnu_debug::__check_singular(__first) && !__gnu_debug::__check_singular(__last)); } which gets it right.