Reputation: 490
In the codebase I inherited, there is a class that look like an iterator (this isn’t the exact code, but the logic is similar).
template <class T>
struct IteratorLike {
T* next() &; // either return a pointer to a valid value or nullptr
};
The way you use it is very similar to the way you use Rust iterators:
IteratorLike<...> it = ...;
while(auto* item = it.next()) {
do_something(*item);
}
How do I convert it to make it compatible with C++ range-based for loop, algorithms, or range-v3? I’m using C++14 (gcc5.5 to be more precise), so I can’t have a sentinel type that is different from the type of the iterator itself.
So far it seems that the easiest way is to store both the iterator and the next value in my wrapper:
template <class T>
class MyIterator {
private:
IteratorLike<T> m_iter;
T* m_value;
public:
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
using iterator_category = std::input_iterator_tag;
reference operator*() const {
assert(m_value && "trying to read past the end of the iterator");
return *m_value;
}
pointer operator->() {
// I’m not sure the assert is needed here
assert(m_value && "trying to read past the end of the iterator");
return m_value;
}
// Prefix increment
MyIterator& operator++() {
m_value = m_iter.next();
return *this;
}
// Postfix increment
MyIterator operator++(int) {
MyIterator tmp = *this;
++(*this);
return tmp;
}
// used by `my_collection.begin()`
explicit MyIterator(IteratorLike<T> iter)
: m_iter{m_iter}
, m_value{this->self.next()}
{}
// missing operator == and operator != as well as the constructor
// used `my_collection.end()
};
However, I fail to understand what my_collection.end()
should return (EDIT: I just check, I can’t default-initialize m_iter
), nor how to have meaningful comparison operators.
Note: I’m basically trying to do the exact reverse of this.
Upvotes: 3
Views: 100
Reputation: 117298
Since IteratorLike
isn't default
constructible, but is obviously copy constructible, you could use the instance you have to construct your end()
iterator too. Example:
// used by `my_collection.begin()`
explicit MyIterator(const IteratorLike<T>& iter) :
m_iter{iter},
m_value{m_iter.next()}
{}
// used by `my_collection.end()`
MyIterator(const IteratorLike<T>& iter, std::nullptr_t) :
m_iter{iter},
m_value{nullptr}
{}
bool operator!=(const MyIterator& rhs) const {
return m_value != rhs.m_value;
}
Then in my_collection
:
template<typename T>
class my_collection {
public:
MyIterator<T> begin() { return MyIterator<T>{itlike}; }
MyIterator<T> end() { return {itlike, nullptr}; }
private:
IteratorLike<T> itlike;
};
Upvotes: 1