2

I'm making my own vector container and I'm trying to implement an iterator that works like the real one, using the C++98 standard.

This is homework so I don't want the answer just a hint as to where I should look and what I should learn to be able to tackle this problem.

So basically I'm trying to make this code work:

ft::vector<int> v (100, 100);
ft::vector<int>::iterator it = v.begin();
ft::vector<int>::const_iterator cit = it;

std::cout << (cit == it) << std::endl; //comparison 1 /// works
std::cout << (it == cit) << std::endl; //comparison 2 /// doesn't compile
std::cout << (cit + 1 == it + 1) << std::endl; //doesn't work
std::cout << (it + 1 == cit + 1) << std::endl; //doesn't work

iterator and const_iterator are typedefs like this:

typedef typename ft::iterator_vector<value_type> iterator;
typedef typename ft::iterator_vector<const value_type> const_iterator;

And value type being the type passed to the vector template.

The first comparison didn't work until I added a user-defined conversion operator in my iterator template to convert iterator<const foo> into iterator<foo> (the other way around actually as pointed out by @TedLyngmo) which is operator iterator_vector<const value_type>() const { return _p; } but the compiler says that I need now to be able to convert a iterator<const foo> into a const iterator<foo> and I have no idea how to proceed.

This is the implementation of my iterator:

template <class T>
class iterator_vector : public ft::iterator<std::random_access_iterator_tag, T> {

public:

    typedef             ft::iterator<std::random_access_iterator_tag, T>    iterator;
    typedef             ft::iterator_traits<iterator>                   iterator_traits;
    typedef typename    iterator_traits::difference_type                difference_type;
    typedef typename    iterator_traits::value_type                     value_type;
    typedef typename    iterator_traits::pointer                        pointer;
    typedef typename    iterator_traits::reference                      reference;
    typedef typename    iterator_traits::iterator_category              iterator_category;

    /*
    ** Member functions
    */

    iterator_vector(pointer p = 0) : _p(p) {}
    ~iterator_vector(void) {}

    operator iterator_vector<const value_type>() const { return _p; }

    iterator_vector& operator++() { ++_p; return *this; }
    iterator_vector operator++(int)
    {
        iterator_vector r = *this;
        ++_p;
        return r;
    }
    iterator_vector& operator--() { --_p; return *this; }
    iterator_vector operator--(int)
    {
        iterator_vector r = *this;
        --_p;
        return r;
    }
    iterator_vector operator+(size_t n) const { return iterator_vector(_p + n); }
    iterator_vector operator-(size_t n) const { return iterator_vector(_p - n); }
    iterator_vector& operator+=(size_t n) { _p += n; return *this; }
    iterator_vector& operator-=(size_t n) { _p -= n; return *this; }
    difference_type operator+(iterator_vector rhs) const { return _p + rhs._p; }
    difference_type operator-(iterator_vector rhs) const { return _p - rhs._p; }
    reference operator*(void) const { return *_p; }
    pointer operator->() const { return _p; }
    reference operator[](size_t n) const { return _p[n]; }
    bool operator==(const iterator_vector& rhs) const { return _p == rhs._p; }
    bool operator!=(const iterator_vector& rhs) const { return _p != rhs._p; }
    bool operator<(const iterator_vector& rhs) const { return _p > rhs._p; }
    bool operator>(const iterator_vector& rhs) const { return _p < rhs._p; }
    bool operator<=(const iterator_vector& rhs) const { return _p <= rhs._p; }
    bool operator>=(const iterator_vector& rhs) const { return _p >= rhs._p; }

    /*
    ** Non-member functions
    */

    friend iterator_vector operator+(size_t n, const iterator_vector& rhs) { return iterator_vector(rhs._p + n); }
    friend iterator_vector operator-(size_t n, const iterator_vector& rhs) { return iterator_vector(rhs._p - n); }

private:

    pointer _p;

};
1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
tvanbesi
  • 25
  • 5
  • Usually you will have an "iterator" and a "const_iterator". Check out here https://en.cppreference.com/w/cpp/container/vector –  Dec 07 '21 at 21:10
  • @Jellyboy what do you mean? I do have a const_iterator already. I use my iterator template but with iterator instead of iterator. Do you mean I should have two separate class template and not reuse the same iterator template ? – tvanbesi Dec 07 '21 at 21:21
  • 1
    @tvanbesi No, you should most probably not have two separate templates if you make template based iterators. – Ted Lyngmo Dec 07 '21 at 21:28
  • 1
    You don't want this conversion. You really really don't. Imagine what would happen if `const char*` was implicitly convertible to `char * const`. Iterators are fancy pointers, and should work like pointers. – n. m. could be an AI Dec 07 '21 at 21:33
  • 1
    @n.1.8e9-where's-my-sharem. I reacted to that too. I updated my answer to address that. – Ted Lyngmo Dec 07 '21 at 21:35

1 Answers1

1

It seems from your comments in your code about what is failing that you are missing a comparison function for when iterator is to the left and the const_iterator is to the right. You could add this free function:

template<typename T>
bool operator==(const iterator_vector<T>& lhs, const iterator_vector<const T>& rhs) {
    // just swap the order here and the implicit conversion from `iterator`
    // to `const_iterator` from `lhs` solves the rest:
    return rhs == lhs;
}

I added a user-defined conversion operator in my iterator template to convert iterator<const foo> into iterator<foo>

No, you added an implicit conversion the other way around. That is, from iterator to const_iterator - which is good!


An alternative could be to make the two iterators friends to not have to implement similar swapping lhs with rhs functions for all your operators. The operators in your template would then become:

// define these before your class template (borrowed from C++11):
template< class T > struct remove_const          { typedef T type; };
template< class T > struct remove_const<const T> { typedef T type; };
//... in your class template:
friend class iterator_vector<typename remove_const<T>::type>;
friend class iterator_vector<const T>;

template<typename U>
bool operator==(const iterator_vector<U>& rhs) const { return _p == rhs._p; }
template<typename U>
bool operator!=(const iterator_vector<U>& rhs) const { return _p != rhs._p; }
template<typename U>
bool operator<(const iterator_vector<U>& rhs) const { return _p > rhs._p; }
template<typename U>
bool operator>(const iterator_vector<U>& rhs) const { return _p < rhs._p; }
template<typename U>
bool operator<=(const iterator_vector<U>& rhs) const { return _p <= rhs._p; }
template<typename U>
bool operator>=(const iterator_vector<U>& rhs) const { return _p >= rhs._p; }
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    What about all other comparison operators? Does OP need to add them too? – n. m. could be an AI Dec 07 '21 at 21:36
  • 1
    @n.1.8e9-where's-my-sharem. Yes, OP would need swapping functions for all operators. I added an alternative. – Ted Lyngmo Dec 07 '21 at 21:45
  • @TedLyngmo And thanks again for the details you added in your edit, but my code must be compatible with the c++98 standard so I won't use remove_const, but maybe I can implement it myself (something with SFINAE I guess) – tvanbesi Dec 07 '21 at 21:58
  • 1
    @tvanbesi Just add the definitions of `remove_const` I put in the answer. They are C++98 compatible. They are just not the `std::remove_const` you get when including `` in later C++ standards. You can put them in your `ft` namespace if you want. – Ted Lyngmo Dec 07 '21 at 21:59
  • 1
    @TedLyngmo Perfect :) – tvanbesi Dec 07 '21 at 22:06
  • 1
    If you accidentally compare two unrelated iterators, the error message could be rather cryptic. You can "fix" it (sort of) this way https://godbolt.org/z/1KK6TjqTo even though it is probably an overkill. – n. m. could be an AI Dec 07 '21 at 22:28
  • @n.1.8e9-where's-my-sharem. Yeah, I know - it's meant to give the same feeling one often got when templates were involved back in -98 :-) Nah, seriously, thanks for the suggestion to make error messages clearer. I'll leave it up to OP to incorporate that into the iterator template.. – Ted Lyngmo Dec 07 '21 at 22:32