This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
basic_const_iterator
's relational operators due to ADL + CWG 2369Section: 24.5.3.5 [const.iterators.ops] Status: New Submitter: Patrick Palka Opened: 2025-03-03 Last modified: 2025-03-09
Priority: Not Prioritized
View all issues with New status.
Discussion:
Consider the example (devised by Hewill Kang)
using RCI = reverse_iterator<basic_const_iterator<vector<int>::iterator>>; static_assert(std::totally_ordered<RCI>);
Checking RCI
is totally_ordered
entails checking
requires (RCI x) { x RELOP x; } for each RELOP in {<, >, <=, >=}
which we expect to be straightforwardly satisfied by reverse_iterator
's
namespace-scope operators (24.5.1.8 [reverse.iter.cmp]):
template<class Iterator1, class Iterator2> constexpr bool operator<( const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y); // etc
But due to ADL we find ourselves also considering the basic_const_iterator
relop friends (24.5.3.5 [const.iterators.ops]/24).
template<input_iterator Iterator> class basic_const_iterator { template<not-a-const-iterator I> friend constexpr bool operator<(const I& x, const basic_const_iterator& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I> // etc };
Before CWG 2369 these candidates would quickly get
discarded since for the second operand RCI clearly isn't convertible to basic_const_iterator
.
But after CWG 2369 implementations must first check these operators' constraints
(with Iterator = vector<int>::iterator
and I = RCI
), which entails
checking totally_ordered<RCI>
recursively, causing the example to be ill-formed.
basic_const_iterator<J>
where J
is
constrained to match Iterator
:
template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator<(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I> // etc
So that deduction fails earlier, before constraints get checked, for a second
operand that isn't a specialization of basic_const_iterator
(or derived from one).
basic_const_iterator
's
operators, but there the recursion was independent of CWG 2369.
Proposed resolution:
This wording is relative to N5001.
Modify 24.5.3.3 [const.iterators.iterator], class template basic_const_iterator
synopsis, as indicated:
namespace std { […] template<input_iterator Iterator> class basic_const_iterator { […] template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator<(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator>(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator<=(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator>=(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; […] }; }
Modify 24.5.3.5 [const.iterators.ops] as indicated:
template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator<(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator>(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator<=(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator>=(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;-23- Let
-24- Effects: Equivalent to:op
be the operator.return x op y.current_;