0

Let's say I want to implement my own collection type like below:

#define ARR_SIZE 20

class IntegersCollection {
public:
  class Iterator {
  public:
    using iterator_category = std::forward_iterator_tag;
    using difference_type = std::ptrdiff_t;
    using value_type = int;
    using pointer = value_type *;
    using reference = value_type &;

    Iterator(pointer ptr) : m_ptr(ptr) {}

    reference operator*() const { return *m_ptr; }
    pointer operator->() { return m_ptr; }
    Iterator &operator++() {
      m_ptr++;
      return *this;
    }
    Iterator operator++(int) {
      Iterator tmp = *this;
      ++(*this);
      return tmp;
    }
    friend bool operator==(const Iterator &a, const Iterator &b) {
      return a.m_ptr == b.m_ptr;
    };
    friend bool operator!=(const Iterator &a, const Iterator &b) {
      return a.m_ptr != b.m_ptr;
    };

  private:
    pointer m_ptr;
  };

  Iterator begin() { return Iterator(&m_data[0]); }
  Iterator end() { return Iterator(&m_data[ARR_SIZE]); }

private:
  int m_data[ARR_SIZE];
};

The problem is, there are many ways users can use a collection type/container type with an iterator. I am not sure if I already implemented all the needed member methods/fields that are needed by a complete and proper iterator.

C++20 introduced the Concept feature, and there are some iterator-related concepts like this one. I am thinking:

Is there something like an "iterator concept" which clearly defines what member methods/fields a particular type of iterator (say forward iterator) should have? I am imagining some thing like below:

template <typename T>
concept is_shape = requires(T v) {
  { v.area() } -> std::floating_point;
};

class Circle {
private:
  double r;
public:
  Circle(double r) { this->r = r; }
  double area() { return r * r * std::numbers::pi_v<float>; };
};

template <is_shape T> float getVolume(T &shape, float height) {
  return shape.area() * height;
}

Circle my_circle(3);
std::cout << getVolume(my_circle, 7.2) << "\n";

We can have an is_shape concept that mandates what all "shapes" should behave, do we have something for iterators, so that if i miss some methods/fields, the C++ can fail me immediately, instead of delaying the failure to a while later, when a real user uses my iterator in a particular way and discovers that my iterator actually misses some important features?

14
  • This question is similar to: How to correctly implement custom iterators and const_iterators?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Sep 27, 2024 at 0:48
  • @Anerdw the question you shared is from 2013, C++20 introduced Concept, as we do have some iterator-related concept here, I believe this question will have some new answers. Commented Sep 27, 2024 at 0:54
  • These are all the requirements depending on the iterator category. The link is to the base iterator and there are links to the other categories in there: en.cppreference.com/w/cpp/named_req/Iterator Commented Sep 27, 2024 at 1:11
  • @NathanOliver i believe this is what we have pre-C++20. Commented Sep 27, 2024 at 1:21
  • Is this any better? stackoverflow.com/q/72405122 Commented Sep 27, 2024 at 2:14

1 Answer 1

1

Is there something like an "iterator concept"

You already linked to the concept std::random_access_iterator, so presumably you know the answer is yes.

That page says it refines std::bidirectional_iterator ..., which itself refines std::forward_iterator ..., which in turn refines std::input_iterator, which ... etc.

... so that if i miss some methods/fields, the C++ can fail me immediately, instead of delaying the failure to a while later, when a real user uses my iterator in a particular way and discovers that my iterator actually misses some important features?

You haven't actually said which features you intend to support. That is, I don't know whether you were intending to implement a random access iterator (you're missing operator[]), or a bidirectional iterator (missing operator--), or what.

Once you pick the iterator concept you intend to implement, just writing eg.

static_assert(std::random_access_iterator<Iterator>);

(as already described in comments) is sufficient.

If the static assert fails, you have your answer: you haven't correctly implemented the concept. Now you need to read the error messages telling you why.

For example, if the compiler tells your it doesn't satisfy

concept semiregular = copyable<_Tp> && default_initializable<_Tp>;
                                       ^

that clearly means you need to add a default constructor (or a default value for your existing constructor argument).

It's because forward_iterator requires incrementable which requires regular which requires semiregular which requires default_initializable - all of which is described in both the error message and the documentation you already linked.

Sign up to request clarification or add additional context in comments.

7 Comments

Do you have a working version of the collection class definition with concept requirements applied and met?
@D.J.Elkind look into your std implementation. Every std collection has a working implementation.
@Red.Wave okay, pls copy and paste your std's implementation then. talk is cheap.
So far we have "how to check X?" Then "how to fix Y when the check fails?" And now "how to check Z instead?" This is supposed to be a question (singular) and answer format, not a discussion board.
Work load belongs the OP. If you can't follow the available header only library in front of you, you're not ready for advanced stuff you're trying.
|

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.