1

My original question

How can I write a C++ class that works with C++20 std::ranges library?

Origin of my problem

I tried to write a facade class with iterators in C++. But my compiler (clang++18) told me that requirements are not met and the deepest unmet requirement is: /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/ranges:946:30: note: because '__adaptor::__is_range_adaptor_closure_fn(__t, __t)' would be invalid: no matching function for call to '__is_range_adaptor_closure_fn'.

My journey towards solving this problem so far

I read through several SO post including:

before I finally ended up here: How to support range adaptors in custom container?

My new questions

While reading the accepted answer of bolov on that last post, several new questions arose at my side:

Example

The following is a simplifyed example of my facade class.

#include <cstddef>
#include <iterator>
#include <unordered_set>

using Set = std::unordered_set<std::ptrdiff_t>;

class MyFacadeClass {
private:
  class Iterator {
  public:
    using iterator_category = std::forward_iterator_tag;
    using value_type        = std::ptrdiff_t;

    Iterator(const std::ptrdiff_t self,
             const Set& a,
             const Set& b,
             const Set::const_iterator it,
             const bool inB = false) :
      pSelf(self),
      pA(a),
      pB(b),
      pIt(it),
      pInB(inB) {}

    std::ptrdiff_t operator*() const { return *pIt; }

    bool operator!=(const Iterator& other) const { return pIt != other.pIt; }

    Iterator& operator++() {
        if (pInB) {
          ++pIt;
          return *this;
        }
        else {
          do {
            ++pIt;
            if (pIt == pA.end()) {
              pIt        = pB.begin();
              pInB = true;
              return *this;
            }
          } while (*pIt == pSelf);
          return *this;
        }
      }


  private:
    const std::ptrdiff_t pSelf;
    const Set& pA;
    const Set& pB;
    Set::const_iterator pIt;
    bool pInB;
  };

  static_assert(std::forward_iterator<Iterator>);

public:
  Iterator begin() const { return Iterator(self, pA, pB, pA.begin()); }
  Iterator end() const { return Iterator(self, pA, pB, pB.end()); }

private:
  const std::ptrdiff_t self;
  const Set& pA;
  const Set& pB;
};

Goal

It would be nice to use c++ syntax like


for(const std::ptrdiff_t& member : myClass | std::views::filter(CONDITION)) {

}

Here we pretend myClass is of Type const MyFacadeClass defined earlier in the code.

Clarification of the listed questions would be nice. Thanks in advance!

7
  • 2
    Maybe duplicate of stackoverflow.com/questions/28832492/… and stackoverflow.com/questions/46695349/… Commented Oct 11, 2024 at 14:53
  • Check cppreference. Comparable value-initialized iterators and double pass guarantee seems to, in a sense, answer both your questions. Even if they don't give any details as to why. Commented Oct 11, 2024 at 15:05
  • 1
    As an aside, having data members be references makes your type harder to interoperate with. Just use pointers, then you can be default constructible Commented Oct 11, 2024 at 15:29
  • Hey 👋 that's me! Look ma' I'm on TV! Commented Oct 11, 2024 at 15:48
  • a comment on my answer: "For posterity, note that the statement "The first fix is to make your iterator default constructible" is correct today, but is considered a bug in the standard and this requirement will be lifted eventually. See open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2325r3.html where the weakly_incrementable concept is discussed, and en.cppreference.com/w/cpp/iterator/weakly_incrementable" Commented Oct 11, 2024 at 15:54

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.