https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120415

Jonathan Wakely <redi at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
      Known to fail|14.3.0, 15.1.1              |

--- Comment #9 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Their hash_set provides an iterator type which meets the C++17 forward iterator
requirements:

template<typename Value,
         typename Extractor = std::identity,
         typename Hasher = std::hash<hash_set_impl::ExtractedType<Value,
Extractor>>,
         typename Equal = std::equal_to<>>
class hash_set
{
protected:
 using PoolIndex = hash_set_impl::PoolIndex;
 static constexpr auto invalidIndex = hash_set_impl::invalidIndex;

public:
 using value_type = Value;

 template<typename HashSet, typename IValue>
 class Iter {
 public:
  using value_type = IValue;
  using difference_type = int;
  using pointer = IValue*;
  using reference = IValue&;
  using iterator_category = std::forward_iterator_tag;

And then they provide a TransformIterator adaptor to wrap other iterators and
provide extra functionality. The problem is the TransformIterator adaptor which
has all these completely unconstrained operators:

 constexpr TransformIterator& operator+=(difference_type n)
 {
  it += n;
  return *this;
 }

 constexpr TransformIterator& operator-=(difference_type n)
 {
  it -= n;
  return *this;
 }

 [[nodiscard]] constexpr friend TransformIterator operator+(TransformIterator
x, difference_type n)
 {
  x += n;
  return x;
 }
 [[nodiscard]] constexpr friend TransformIterator operator+(difference_type n,
TransformIterator x)
 {
  x += n;
  return x;
 }

 [[nodiscard]] constexpr friend TransformIterator operator-(TransformIterator
x, difference_type n)
 {
  x -= n;
  return x;
 }

 [[nodiscard]] constexpr friend difference_type operator-(const
TransformIterator& x, const TransformIterator& y)
 {
  return x.it - y.it;
 }

 [[nodiscard]] constexpr reference operator[](difference_type n)
 {
  return *(*this + n);
 }

 [[nodiscard]] constexpr auto operator<=>(const TransformIterator& other) const
 {
  return it <=> other.it;
 }


The C++20 library sees these and thinks that it must be a random access
iterator, because it provides all the operations that a random access iterator
provides. But when it tries to use those operations on a
TransformIterator<hash_set::Iter> they fail to compile.

This code is not ready to be compiled as C++20.

There are a few ways to fix it, they could add a requires-clause (or enable_if
constraint) to each of those operators in TransformIterator, so they can only
be used if the underlying iterator being adapted supports it.

Or they could define TransformIterator::iterator_concept to stop the C++20
library trying to deduce that for itself.

Or they could specialize std::sized_sentinel_for for TransformIterator.

I'm testing those now ...

Reply via email to