1

I am trying to learn C++ and would like to understand iterators better. To that end, I tried to write an STL compatible iterator for a custom class (called StrBlob which holds strings, defined in the wonderful C++ Primer). Eventually, the following iterator definition worked:

class StrBlob::iterator
    : public std::iterator<std::input_iterator_tag,  // iterator_category
                           std::string,              // value_type
                           std::string,              // difference_type
                           const std::string*,       // pointer
                           std::string               // reference
                           > {
   public:
    iterator() : curr(0) {}
    iterator(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
    iterator& operator++();  // prefix operators
    iterator& operator--();
    iterator operator++(int);  // postfix operators
    iterator operator--(int);
    std::string& operator*() const;
    std::string* operator->() const;
    bool operator==(const iterator& b) const { return curr == b.curr; }
    bool operator!=(const iterator& b) const { return curr != b.curr; }

   private:
    std::shared_ptr<std::vector<std::string>> check(std::size_t,
                                                    const std::string&) const;
    std::weak_ptr<std::vector<std::string>> wptr;
    std::size_t curr;
};

Initially I tried for a long time in vain to design the iterator with the right type definitions myself. This didn't work, so I simply provided an inheritance to std::iterator as mentioned in cppreference.

Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?

(I tried to understand Stackoverflow answers to related questions about iterator design but they were a bit to abstract for me.)

fabian
  • 1,413
  • 1
  • 13
  • 24
  • An iterator is a pointer to an element. ++ on the iterator must point to the next element. If this is, say, a continuous stream, it's simply ++ing the raw pointer. If it's a list, it's getting the next element. It's an abstraction where the underlying details are hidden. – Michael Chourdakis Jul 13 '19 at 14:04
  • 1
    [std::iterator](https://en.cppreference.com/w/cpp/iterator/iterator) is deprecated in C++17, and only provides typedef. – Jarod42 Jul 13 '19 at 14:06

2 Answers2

2

Your code is bugged, std::string is not a suitable difference type (difference_type is the difference between two iterators so normally its std::ptrdiff_t), nor is std::string a suitable reference type (should be std::string&).

Here's the correct typedefs, you can use these instead of inheritance.

typedef std::input_iterator_tag iterator_category;
typedef std::string value_type;
typedef std::ptrdiff_t difference_type;
typedef std::string* pointer;
typedef std::string& reference;
john
  • 85,011
  • 4
  • 57
  • 81
1

Can I write the class without inheritance at all? If so, how would I have to rewrite the code above?

Yes, std::iterator (deprecated in C++17), only provides typedef.

So you can add those typedef yourself instead of using inheritance.

class StrBlob::iterator
{
public:
    using iterator_category = std::input_iterator_tag;
    using value_type = std::string;
    using difference_type = std::string;// should be `std::ptrdiff_t`
    using pointer = const std::string*; // const should probably be dropped, unless you do const_iterator
    using reference = std::string; // should be std::string&

// ...
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302