Implementing erase() for a custom std::map iterator - c++

I have written a class to wrap around an std::map, along with an iterator class to allow controlling accessing the map's key:
#include <map>
class Container
{
public:
class iterator
{
using map = std::map < int, int >;
map::const_iterator _iter;
public:
iterator() {}
iterator(map::iterator iter) :_iter(iter) {}
iterator(map::const_iterator iter) :_iter(iter) {}
iterator(const iterator& b) :_iter(b._iter) {}
iterator& operator=(const iterator& b)
{
_iter = b._iter;
return *this;
}
iterator& operator++()
{
++_iter;
return *this;
}
iterator operator++(int)
{
return iterator(_iter++);
}
const map::key_type first()
{
return std::abs(_iter->first);
}
const int second()
{
return _iter->second;
}
bool operator==(const iterator& b)
{
return _iter == b._iter;
}
bool operator!=(const iterator& b)
{
return _iter != b._iter;
}
};
void insert(int key, int value)
{
_map[-key] = value; // Key is modified from user, hence need for wrapper
}
iterator begin()
{
return iterator(_map.begin());
}
iterator end()
{
return iterator(_map.end());
}
iterator find(int key)
{
return iterator(_map.find(key));
}
iterator erase(iterator iter)
{
return iterator(_map.erase(iter));
}
private:
std::map<int, int> _map;
};
My required operations are:
Iterating
Finding
Erasing
Getting key
Getting value
I would like usage to be like:
int main()
{
Container o;
o.insert(-1, 100);
o.insert(-2, 200);
o.insert(-3, 300);
o.insert(-4, 300);
for (Container::iterator i = o.begin(); i != o.end();)
{
if (i.first() == -2)
{
std::cout << i.first() << " " << i.second() << std::endl;
++i;
}
else
{
i = o.erase(i);
}
}
}
so that I can erase elements and continue iterating.
However, I am struggling to implement the container's erase() method because the input is my custom iterator type, not the map's key_type.

Declare class Container as friend class of Iterator, and access the _iter in erase function.
class iterator
{
friend class Container;
//...
}
iterator Container::erase(iterator iter)
{
return iterator(_map.erase(iter._iter));
}

Related

how to write forward iterator using private std::vector base class

I need a vector class that exposes a small subset of the std::vector API. Everything works except range-based for. Here my attempt at implementing a forward iterator, which however does not compile.
#include <vector>
#include <iostream>
template <class T>
class OwningVector : private std::vector<T*> {
using super = std::vector<T*>;
public:
OwningVector() = default;
~OwningVector()
{
for (T* e : *this)
delete e;
super::clear();
}
OwningVector(const OwningVector& other)
: super()
{
super::reserve(other.size());
for (T* e : other)
super::emplace_back(e->clone());
}
OwningVector& operator=(const OwningVector& other)
{
if (this == &other)
return *this;
OwningVector ret(other);
swap(*this, ret);
return *this;
}
void emplace_back(T* e) { super::emplace_back(e); }
size_t size() const { return super::size(); }
T* const& operator[](int i) const { return super::operator[](i); }
T* const& at(int i) const { return super::at(i); }
const T* back() const { return super::back(); }
// Here the questionable part of my code:
class Iterator : public std::vector<T*>::iterator {};
Iterator begin() const { return super::begin(); }
Iterator end() const { return super::end(); }
Iterator begin() { return super::begin(); }
Iterator end() { return super::end(); }
};
class A {
public:
A(int m) : m(m) {}
int m;
};
int main() {
OwningVector<A> v;
v.emplace_back(new A(1));
for (const A*const a: v)
std::cout << a->m << std::endl;
}
Compilation fails:
h.cpp: In instantiation of ‘OwningVector<T>::Iterator OwningVector<T>::begin() [with T = A]’:
h.cpp:56:27: required from here
h.cpp:43:43: error: could not convert ‘((OwningVector<A>*)this)->OwningVector<A>::<anonymous>.std::vector<A*, std::allocator<A*> >::begin()’ from ‘std::vector<A*, std::allocator<A*> >::iterator’ to ‘OwningVector<A>::Iterator’
43 | Iterator begin() { return super::begin(); }
| ~~~~~~~~~~~~^~
| |
| std::vector<A*, std::allocator<A*> >::iterator
h.cpp: In instantiation of ‘OwningVector<T>::Iterator OwningVector<T>::end() [with T = A]’:
h.cpp:56:27: required from here
h.cpp:44:39: error: could not convert ‘((OwningVector<A>*)this)->OwningVector<A>::<anonymous>.std::vector<A*, std::allocator<A*> >::end()’ from ‘std::vector<A*, std::allocator<A*> >::iterator’ to ‘OwningVector<A>::Iterator’
44 | Iterator end() { return super::end(); }
| ~~~~~~~~~~^~
| |
| std::vector<A*, std::allocator<A*> >::iterator
class Iterator : public std::vector<T*>::iterator {};
Why? This looks like a certain other language's way of doing that. It's also not guaranteed to work because vector<T*>::iterator may actually be a pointer type, in which case you can't derive from it.
using Iterator = typename super::iterator;
using ConstIterator = typename super::const_iterator;
ConstIterator begin() const { return super::begin(); }
ConstIterator end() const { return super::end(); }
Iterator begin() { return super::begin(); }
Iterator end() { return super::end(); }
This works. The typename is required up to C++17, can be dropped for C++20.

Create contiguous_iterator for custom class

Summary
I have a custom array class:
template<typename T, int SIZE>
class Array {
private:
T mArray[SIZE];
};
To enable support for std algorithms, with ranges, I want to create an iterator for this class. It would seem that std::contiguous_iterator would be the optimal choice since I can guarantee contiguous memory layout for the data. Following the iterator tutorial I should create a class inside this class. However, I should somehow be (quoted) "For example, instead of the std::forward_iterator_tag tag you would mark your iterator with the std::forward_iterator concept.".
I have a hard time figuring out what the syntax would look like for this, and I have been unable to find a post on the web showcasing this.
Question
How do I complete the following code snippet to implement std::contiguous_iterator for my Array<T,S> class?:
import <iterator>;
template<typename T, int SIZE>
class Array {
public:
const T& operator[](int i) { return mArray[i]; }
T& operator[](int i) { return mArray[i]; }
private:
T mArray[SIZE];
public:
struct Iterator {
Iterator(T* ptr) : mPtr(ptr) {}
private:
T* mPtr;
};
Iterator begin() { return Iterator(&mArray[0]); }
Iterator end() { return Iterator(&mArray[SIZE]); }
};
NOTE: There is a lot of operator overloads. An answer is not required to provide all of them. I just need an example syntax for where to place the concept, then I can probably figure out the rest.
As far as I could tell you need typedefs on the iterator class, so simply using a pointer was not sufficient. Here is an example:
#include <iterator>
#include <algorithm>
template<typename T, int SIZE>
class Array {
public:
const T& operator[](int i) const { return mArray[i]; }
T& operator[](int i) { return mArray[i]; }
private:
T mArray[SIZE];
public:
struct iterator
{
using difference_type=std::ptrdiff_t;
using value_type=std::remove_cv_t<T>;
using pointer=T*;
using reference=T&;
using iterator_category=std::random_access_iterator_tag;
using iterator_concept=std::contiguous_iterator_tag;
using self_type=iterator;
iterator(T *x) : ptr(x) {}
T operator*() { return *ptr; }
T operator->() { return ptr; }
difference_type operator-(const iterator& rhs) { return ptr-rhs.ptr; }
iterator& operator ++() { ++ptr; return *this;}
bool operator !=(const iterator& rhs) { return ptr != rhs.ptr; }
private:
T * ptr;
};
iterator begin() { return &mArray[0]; }
iterator end() { return &mArray[SIZE]; }
};
int foo(Array<int, 7>& a)
{
int sum;
for (auto x : a)
{
sum+=x;
}
return sum;
}
int goo(Array<int, 7>& a, int x)
{
auto ret=std::find(a.begin(), a.end(), x);
if (ret!=a.end()) return *ret;
return 0;
}
Note that you would likely need const_iterator and reverse_iterators for const and non-const ...
Credits
Thanks to #glenn-teitelbaum for pointing me in the right direction. I think I managed to figure out how to do this. It took a long time, so this will hopefully save someone else that trouble.
[Answering my own question]
The iterator should comply with the std::contiguous_iterator concept, so I looked at cppreference for the necessary parts. The concept is defined like this:
template<class I>
concept contiguous_iterator =
std::random_access_iterator<I> &&
std::derived_from</*ITER_CONCEPT*/<I>, std::contiguous_iterator_tag> &&
std::is_lvalue_reference_v<std::iter_reference_t<I>> &&
std::same_as<
std::iter_value_t<I>, std::remove_cvref_t<std::iter_reference_t<I>>
> &&
requires(const I& i) {
{ std::to_address(i) } ->
std::same_as<std::add_pointer_t<std::iter_reference_t<I>>>;
};
So in order to implement this concept, I must first also implement std::random_access_iterator, std::is_derived_from<[...]>, std::is_lvalue_reference_v<[...]>, and so on. This is a recursive process, especially since std::random_access_iterator builds on top of 4 other iterator types. Since this is a C++20 question, I aim to use C++20 features as much as possible (since it greatly simplifies the implementation). This is the complete iterator implementation:
template<typename T, int SIZE>
class Array
{
public:
class Iterator
{
public:
using iterator_category = std::contiguous_iterator_tag;
using iterator_concept = std::contiguous_iterator_tag;
//using difference_type = std::ptrdiff_t; // Likely the same
using difference_type = typename std::iterator<
std::contiguous_iterator_tag, T>::difference_type;
//using value_type = T;
using value_type = std::remove_cv_t<T>; // Using `T` seems sufficient
using pointer = T*;
using reference = T&;
// constructor for Array<T,S>::begin() and Array<T,S>::end()
Iterator(pointer ptr) : mPtr(ptr) {}
// std::weakly_incrementable<I>
Iterator& operator++() { ++mPtr; return *this; }
Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; }
Iterator() : mPtr(nullptr/*&mArray[0]*/) {} // TODO: Unsure which is correct!
// std::input_or_output_iterator<I>
reference operator*() { return *mPtr; }
// std::indirectly_readable<I>
friend reference operator*(const Iterator& it) { return *(it.mPtr); }
// std::input_iterator<I>
// No actions were needed here!
// std::forward_iterator<I>
// In C++20, 'operator==' implies 'operator!='
bool operator==(const Iterator& it) const { return mPtr == it.mPtr; }
// std::bidirectional_iterator<I>
Iterator& operator--() { --mPtr; return *this; }
Iterator operator--(int) { Iterator tmp = *this; --(*this); return tmp; }
// std::random_access_iterator<I>
// std::totally_ordered<I>
std::weak_ordering operator<=>(const Iterator& it) const {
return std::compare_three_way{}(mPtr, it.mPtr);
// alternatively: `return mPtr <=> it.mPtr;`
}
// std::sized_sentinel_for<I, I>
difference_type operator-(const Iterator& it) const { return mPtr - it.mPtr; }
// std::iter_difference<I> operators
Iterator& operator+=(difference_type diff) { mPtr += diff; return *this; }
Iterator& operator-=(difference_type diff) { mPtr -= diff; return *this; }
Iterator operator+(difference_type diff) const { return Iterator(mPtr + diff); }
Iterator operator-(difference_type diff) const { return Iterator(mPtr - diff); }
friend Iterator operator+(difference_type diff, const Iterator& it) {
return it + diff;
}
friend Iterator operator-(difference_type diff, const Iterator& it) {
return it - diff;
}
reference operator[](difference_type diff) const { return mPtr[diff]; }
// std::contiguous_iterator<I>
pointer operator->() const { return mPtr; }
using element_type = T;
private:
T* mPtr;
};
// === STATIC ASSERTS ===
// - to verify correct Iterator implementation!
static_assert(std::weakly_incrementable<Iterator>);
static_assert(std::input_or_output_iterator<Iterator>);
static_assert(std::indirectly_readable<Iterator>);
static_assert(std::input_iterator<Iterator>);
static_assert(std::incrementable<Iterator>);
static_assert(std::forward_iterator<Iterator>);
static_assert(std::bidirectional_iterator<Iterator>);
static_assert(std::totally_ordered<Iterator>);
static_assert(std::sized_sentinel_for<Iterator, Iterator>);
static_assert(std::random_access_iterator<Iterator>);
static_assert(std::is_lvalue_reference_v<std::iter_reference_t<Iterator>>);
static_assert(std::same_as<std::iter_value_t<Iterator>,
std::remove_cvref_t<std::iter_reference_t<Iterator>>>);
static_assert(std::contiguous_iterator<Iterator>);
const T& operator[](int i) const {
if (i < 0 || i >= SIZE) {
throw std::runtime_error("Array index out of bounds");
}
return mArray[i];
}
T& operator[](int i) {
if (i < 0 || i >= SIZE) {
throw std::runtime_error("Array index out of bounds");
}
return mArray[i];
}
Iterator begin() { return Iterator(&mArray[0]); }
Iterator end() { return Iterator(&mArray[SIZE]); }
private:
T mArray[SIZE];
};
// Check that the Array class can be used as a contiguous_range.
static_assert(std::ranges::contiguous_range<Array<int, 10>>);
NOTE: using element_type = T; was necessary because of a bug in the specification, which might be fixed. I found information about that here. Adding this fixed issue with std::to_address<Iterator> not being able to compile, and was the last missing piece in going from std::random_access_iterator to std::contiguous_iterator.
Testing
I did not perform a complete testing suite with all algorithms, but I chose a few which depend on ranges and std::random_access_iterator. It all runs smoothly. I also depend on building standard library headers as module units, because I want to showcase how C++20 features work together.
import <stdexcept>;
import <iostream>;
import <iterator>;
import <algorithm>;
import <random>;
#include <memory> // fails to build header unit!
template<typename T, int SIZE>
class Array
{
[...]
};
int main()
{
Array<int, 10> arr;
for (int i = 0; i < 10; i++) arr[i] = i;
// I need to call std::ragnes::shuffle since that depends on
// std::random_access_iterator, so that is a minimum.
// https://en.cppreference.com/w/cpp/algorithm/ranges/shuffle
std::random_device rd;
std::mt19937 gen{rd()};
std::cout << "before random shuffle:\n";
for (auto& i : arr) std::cout << i << ' ';
std::ranges::shuffle(arr, gen);
std::cout << "\nafter random shuffle:\n";
for (auto& i : arr) std::cout << i << ' ';
std::cout << '\n';
// Also std::ranges::stable_sort is a good check (also random_access_iterator):
// https://en.cppreference.com/w/cpp/algorithm/ranges/stable_sort
std::cout << "after stable_sort:\n";
std::ranges::stable_sort(arr);
for (auto& i : arr) std::cout << i << ' ';
std::cout << '\n';
auto [min,max] = std::ranges::minmax(arr);
std::cout << "min: " << min << ", max: " << max << '\n';
return 0;
}

Overload operator++ for encapsulated standard container

I am encapsulating a std::list to make it safely iterable when iterating can potentially mark contents as 'invalid', and 'invalid' contents are skipped in the iteration. Specifically, during the iteration the current object could schedule itself or other objects for removal from the list and mark those objects as invalid. The list is then periodically cleaned of invalid objects.
How do I define an increment operator to make range-based for loops work correctly? Here is my class:
template <typename T> class DeferredCleanupList
{
public:
DeferredCleanupList() {
(void)static_cast<Valid *>((T)0);
}
virtual ~DeferredCleanupList() {}
typedef typename std::list<T>::iterator iterator;
iterator begin() {
iterator it = container.begin();
if ((*it)->valid())
return it;
return next(it);
}
iterator next(iterator it) {
do {
++it;
}
while (it != end() && !(*it)->valid());
return it;
}
iterator end() { return container.end(); }
// to be implemented:
// typedef typename std::list<T>::const_iterator const_iterator ;
// const_iterator cbegin() const { return container.cbegin(); }
// const_iterator cend() const { return container.cend(); }
// const_iterator cnext() const { ??? }
size_t size() const { return container.size(); }
void add(T *ptr) { container.push_front(ptr); }
void remove(T *ptr) { ptr->invalidate(); }
// called occasionally
void delete_invalid() {
for (auto it = container.begin(); it != container.end(); ) {
auto ptr = *it;
if (ptr->valid())
++it;
else {
delete ptr;
it = container.erase(it);
}
}
}
private:
DeferredCleanupList(const DeferredCleanupList&);
DeferredCleanupList& operator=(const DeferredCleanupList&);
std::list<T> container;
};
My current test case is something like:
int main() {
class D : public Valid {};
DeferredCleanupList<D *> list;
for (auto it = list.begin(); it != list.end(); it = list.next(it)); // works
for (auto ptr : list); // iterates, but doesn't call list.next(it)
}
EDIT:
After some trial and error, I wrote this iterator wrapper based on the suggestions in the comments:
template <typename T> class DeferredCleanupList
{
public:
class iterator {
public:
iterator(typename std::list<T>::iterator it, DeferredCleanupList<T>& ls) : it(it), list(ls) {}
iterator& operator=(const iterator& rhs) { it = rhs; return *this; }
iterator& operator++() {
do {
++it;
}
while (it != list.end().it && !(*it)->valid());
return *this;
}
friend bool operator==(const iterator& lhs, const iterator& rhs) { return lhs.it == rhs.it; }
friend bool operator!=(const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); }
T& operator*() { return *it; }
private:
typename std::list<T>::iterator it;
DeferredCleanupList& list;
};
iterator begin() {
iterator it = iterator(container.begin(), *this);
if (it == end() || (*it)->valid())
return it;
return ++it;
}
iterator end() { return iterator(container.end(), *this); }
}
It appears to work perfectly in all the test cases I throw at it. Am I missing anything obvious with this approach? Is there a more elegant solution?
You can not do it within the container class. You should implement your own iterator and implement the behavior you want in it's prefix increment operator. You can do it several ways including inheriting one from STL or creating your own wrapper for std::list iterator.
Actually to implement your desired behavior in iterator you need to provide to your custom iterator class the end iterator of container.
I would not recommend you to implement your own iterators unless your design actually good with them. Unnecessary complications almost always lead to even more unnecessary complications.
I've modified your code so it actually compiles and implemented std::list iterator wrapper to behave as you planned.
#include <iostream>
#include <list>
#include <iterator>
using namespace std;
class Valid {};
template<typename _Tp>
struct myIteratorWrapper
{
typedef myIteratorWrapper<_Tp> _Self;
typedef _Tp value_type;
typedef _Tp* pointer;
typedef _Tp& reference;
typedef typename std::list<_Tp>::iterator listIterator;
listIterator it;
listIterator itEnd;
myIteratorWrapper(const listIterator& listIterArg) _GLIBCXX_NOEXCEPT
: it(listIterArg)
{}
myIteratorWrapper(const listIterator& itBegin,
const listIterator& itEnd) _GLIBCXX_NOEXCEPT
: it(itBegin), itEnd(itEnd)
{}
reference
operator*() const _GLIBCXX_NOEXCEPT
{ return *it; }
pointer
operator->() const _GLIBCXX_NOEXCEPT
{ return &(*it); }
/* Change logic of this method as you wish, but keep the signature */
_Self&
operator++() _GLIBCXX_NOEXCEPT
{
do
{
++it;
}while (it != itEnd && !(*it)->valid());
return *this;
}
bool
operator==(const _Self& __x) const _GLIBCXX_NOEXCEPT
{ return it == __x.it; }
bool
operator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT
{ return it != __x.it; }
};
template <typename T> class DeferredCleanupList
{
public:
DeferredCleanupList() {
(void)static_cast<Valid *>((T)0);
}
virtual ~DeferredCleanupList() {}
typedef myIteratorWrapper<T> iterator;
iterator begin() {
iterator it(container.begin(), container.end());
return it;
}
iterator next(iterator it) {
return ++it;
}
iterator end() { return container.end(); }
size_t size() const { return container.size(); }
void add(T ptr) { container.push_front(ptr); }
void remove(T ptr) { ptr->invalidate(); }
// called occasionally
void delete_invalid() {
for (auto it = container.begin(); it != container.end(); ) {
auto ptr = *it;
if (ptr->valid())
++it;
else {
delete ptr;
it = container.erase(it);
}
}
}
private:
DeferredCleanupList(const DeferredCleanupList&);
DeferredCleanupList& operator=(const DeferredCleanupList&);
std::list<T> container;
};
class D : public Valid {
bool isValid;
std::string myName;
public:
D(std::string myName, bool arg = false)
: myName(myName), isValid(arg) {}
bool valid() const
{ return isValid; }
const std::string& whoAmI() const
{ return myName; }
};
int main() {
D o1("o1", true);
D o2("o2");
D o3("o3", true);
D o4("o4");
D o5("o5", true);
DeferredCleanupList<D *> list;
list.add(&o1);
list.add(&o2);
list.add(&o3);
list.add(&o4);
list.add(&o5);
for (auto ptr : list)
{
std::cout << ptr->whoAmI() << std::endl;
}
}
Output:
o5
o3
o1
From http://en.cppreference.com/w/cpp/language/range-for
The above syntax produces code equivalent to the following (__range,
__begin and __end are for exposition only):
{
auto && __range = range_expression ;
for (auto __begin = begin_expr, __end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
ie you must have the preincrement operator defined.

How can I properly reference the first vector container in my iterator?

Right now, I am trying to create a template class Set that holds a generic type <T> with an iterator. Although I don't quite understand what the purpose is, I'm suppoed to create what is called an "end sentinel" for the iterator as so:
while(start != end)
cout << start++ << endl;
start and end refer to what I believe to be the beginning and ending of the vector. So far, I've created a template class, created an iterator within it. In my main, I insert 10 integers and then try to use my "end sentinel". My code looks like this:
#include <iostream>
#include <vector>
using namespace std;
template <class T>
class Set{
vector<T> theSet;
public:
Set() {}
Set(const Set& s): theSet(s.theSet){}
~Set(){theSet.clear();}
void insert(T t){
cout << "inserted " << t << endl;
theSet.push_back(t);
}
class iterator;
friend class iterator;
class iterator{
Set<T>& s;
int index;
public:
iterator(Set<T>& is) : s(is), index(0) {}
iterator(Set<T>& is, bool) : s(is) {}
T operator*(){
return s.theSet.at(index);
}
T operator++(){
return ++s.theSet.at(index);
}
T operator++(int){
return s.theSet.at(index)++;
}
bool operator!=(const iterator& ri)const {return index!=ri.index;}
};
iterator begin() {return iterator (*this);}
//Create the end sentinel:
iterator end() {return iterator (*this, true); }
};
int main()
{
Set<int> other;
for(int i=0; i<10; ++i){
other.insert(i);
}
/*
for(Set<int>::iterator start = other.begin(); start != other.end(); start++){
cout << *start << endl;
}
cout << "\n\n Program ends succesfully" << endl;
*/
Set<int>::iterator start = other.begin();
Set<int>::iterator end = other.end();
while(start != end){
cout << start++ << endl;
}
return 0;
}
The problem comes when I reference the start and end at the end of my class:
iterator begin() {return iterator (*this);}
//Create the end sentinel:
iterator end() {return iterator (*this, true); }
It appears, begin() returns the iterator with the first constructor, and end() returns the iterator with the second constructor as the latter takes in two parameters. However, the second constructor I was given looks like
iterator(Set<T>& is, bool) : s(is) {}
I'm not sure how this references the "end" of the containers. How can I reference it in the correct way?
Here some example code. This is just for demonstration purposes! This code is not efficient and a lot of features are missing for the real world use!
#include <iostream>
using namespace std;
template <typename T>
class Set {
int sz;
T* elems;
public:
Set() : sz{0}, elems{nullptr} {}
Set(int n) : sz{n}, elems{new T[n]} {}
~Set() { delete[] elems; }
class Iterator;
Iterator begin() { return Iterator{elems}; }
Iterator end() {
return Iterator{elems + sz};
} // points to past-the-end of elems
// for example if sz=5:
// 1 2 3 4 5 | 6
// | |
// begin() end()
Iterator insert(T t) {
// check if the element t exists in the set
// -> if so, then return the pointer to the existing element
// -> otherwise return the pointer to the newly inserted element
// BUT:this is very inefficient !!!
// in a real world code one would use
// some kind of binary try for holding
// the keys/values of the set
for (int i = 0; i < sz; i++)
if (elems[i] == t)
return Iterator{elems+i};
T* new_elems = new T[sz + 1];
for (int i = 0; i < sz; ++i)
new_elems[i] = elems[i];
new_elems[sz++] = t;
delete[] elems;
elems = new_elems;
return Iterator{elems+sz};
}
class Iterator {
public:
Iterator() : p{nullptr} {}
Iterator(T* pp) : p{pp} {}
T* operator++() { return ++p; } // pre-increment, ex. ++iter
T* operator++(int) { return p++; } // post-increment, ex. iter++
T& operator*() { return *p; }
const T& operator*() const { return *p; }
bool operator==(const Iterator& rhs) const { return p == rhs.p; }
bool operator!=(const Iterator& rhs) const { return !this->operator==(rhs); }
private:
T* p;
}; // Iterator
}; // Set
int main() {
Set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
for (Set<int>::Iterator it=s.begin(); it != s.end(); ++it)
std::cout << *it << std::endl;
}
Here is the output:
https://wandbox.org/permlink/LT4F2CAkZhFCCcZw

MSVC compiler bug causing error with iterators and friend functions?

I've been working on a small-scale test to see if I can figure out some compiler-specific larger-scale problems with a larger container. The following code works fine in GCC but causes the following error code in Visual Studio 2010 and 2013:
"Error 1 error C2675: unary '--' : 'std::iterator' does not define this operator or a conversion to a type acceptable to the predefined operator d:\programming\workspaces\adl_test\main.cpp 127 1 adL_test_msvc"
Test code is as follows:
#include <iostream>
#include <iterator>
namespace nsp
{
template <class element_type, class element_allocator_type = std::allocator<element_type> >
class test_container
{
private:
element_type numbers[50];
friend class iterator;
friend class reverse_iterator;
public:
class reverse_iterator; //forward decl
class iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
element_type *i;
template <class distance_type>
friend void advance(reverse_iterator &rit, distance_type n);
public:
iterator() {}
iterator(element_type &_i)
{
i = &(_i);
}
element_type & operator *()
{
return *i;
}
iterator & operator = (const element_type &source_i)
{
i = &(source_i);
return *this;
}
iterator & operator = (const iterator &source)
{
i = source.i;
return *this;
}
bool operator != (const iterator rh)
{
return i != rh.i;
}
iterator & operator ++()
{
++i;
return *this;
}
iterator & operator --()
{
--i;
return *this;
}
template <class distance_type>
friend void advance(iterator &it, distance_type n)
{
it.i += n;
}
friend typename std::iterator_traits<iterator>::difference_type distance(const iterator &first, const iterator &last)
{
return last.i - first.i;
}
};
class reverse_iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
iterator it;
public:
reverse_iterator(element_type &_i)
{
it.i = _i;
}
reverse_iterator(const iterator &source)
{
it = source;
}
element_type & operator *()
{
return *it;
}
element_type & operator = (const reverse_iterator &source)
{
it = source.it;
return *this;
}
element_type & operator = (const iterator &source)
{
it = source;
return *this;
}
bool operator != (const iterator rh)
{
return it != rh.it;
}
reverse_iterator & operator ++()
{
--it;
return *this;
}
reverse_iterator & operator --()
{
++it;
return *this;
}
template <class distance_type>
friend void advance(reverse_iterator &rit, distance_type n)
{
rit.it.i -= n;
}
friend typename std::iterator_traits<reverse_iterator>::difference_type distance(const reverse_iterator &first, const reverse_iterator &last)
{
return distance(last.it, first.it);
}
};
iterator begin()
{
return iterator(numbers[0]);
}
iterator end()
{
return iterator(numbers[50]);
}
};
}
int main(int argc, char **argv)
{
nsp::test_container<int> stuff;
int counter = 0;
for (nsp::test_container<int>::iterator it = stuff.begin(); it != stuff.end(); ++it)
{
*it = counter++;
}
nsp::test_container<int>::iterator it = stuff.begin(), it2 = stuff.begin();
using namespace std;
std::cout << *it << std::endl;
++it;
--it;
++it;
std::cout << *it << std::endl;
advance(it, 2);
std::cout << *it << std::endl;
std::advance(it, 2);
std::cout << *it << std::endl;
int distance_between = distance(it2, it);
std::cout << distance_between << std::endl;
nsp::test_container<int>::reverse_iterator rit = it, rit2 = it2;
--rit;
++rit;
advance(rit, -2);
distance_between = distance(rit2, rit);
std::cout << distance_between << std::endl;
std::cin.get();
return 0;
}
Obviously the -- operator works fine on iterator as is demonstrated in the code, but when called from reverse_iterator MSVC creates an error, despite reverse_iterator being a friend. Why?
And what is the workaround for this bug?
Don't suggest different ways of approaching the code (ie. modifying i directly instead of calling -- operator), this is a test case, not actual working code. It does not represent the complexity of the actual code and I won't explain to you why the actual code works in this way because I don't have time.
Thanks to immibus, the answer has been found:
GCC believes 'iterator' within reverse_iterator refers to test_container::iterator, whereas (for some reason) MSVC 2010-2013 believes it refers to the base class.
The solution is to be more specific when specifying iterator within reverse_iterator - using "typename test_container::iterator" instead of "iterator".
Corrected code:
#include <iostream>
#include <iterator>
namespace nsp
{
template <class element_type, class element_allocator_type = std::allocator<element_type> >
class test_container
{
private:
element_type numbers[50];
friend class iterator;
friend class reverse_iterator;
public:
class reverse_iterator; //forward decl
class iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
element_type *i;
template <class distance_type>
friend void advance(reverse_iterator &rit, distance_type n);
public:
iterator() {}
iterator(element_type &_i)
{
i = &(_i);
}
element_type & operator *()
{
return *i;
}
iterator & operator = (const element_type &source_i)
{
i = &(source_i);
return *this;
}
iterator & operator = (const iterator &source)
{
i = source.i;
return *this;
}
bool operator != (const iterator rh)
{
return i != rh.i;
}
iterator & operator ++()
{
++i;
return *this;
}
iterator & operator --()
{
--i;
return *this;
}
template <class distance_type>
friend void advance(iterator &it, distance_type n)
{
it.i += n;
}
friend typename std::iterator_traits<iterator>::difference_type distance(const iterator &first, const iterator &last)
{
return last.i - first.i;
}
};
class reverse_iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
typename test_container::iterator it;
public:
reverse_iterator(element_type &_i)
{
it.i = _i;
}
reverse_iterator(const typename test_container::iterator &source)
{
it = source;
}
element_type & operator *()
{
return *it;
}
element_type & operator = (const reverse_iterator &source)
{
it = source.it;
return *this;
}
element_type & operator = (const typename test_container::iterator &source)
{
it = source;
return *this;
}
bool operator != (const typename test_container::iterator rh)
{
return it != rh.it;
}
reverse_iterator & operator ++()
{
--it;
return *this;
}
reverse_iterator & operator --()
{
++it;
return *this;
}
template <class distance_type>
friend void advance(reverse_iterator &rit, distance_type n)
{
rit.it.i -= n;
}
friend typename std::iterator_traits<reverse_iterator>::difference_type distance(const reverse_iterator &first, const reverse_iterator &last)
{
return distance(last.it, first.it);
}
};
iterator begin()
{
return iterator(numbers[0]);
}
iterator end()
{
return iterator(numbers[50]);
}
};
}
int main(int argc, char **argv)
{
nsp::test_container<int> stuff;
int counter = 0;
for (nsp::test_container<int>::iterator it = stuff.begin(); it != stuff.end(); ++it)
{
*it = counter++;
}
nsp::test_container<int>::iterator it = stuff.begin(), it2 = stuff.begin();
using namespace std;
std::cout << *it << std::endl;
++it;
--it;
++it;
std::cout << *it << std::endl;
advance(it, 2);
std::cout << *it << std::endl;
std::advance(it, 2);
std::cout << *it << std::endl;
int distance_between = distance(it2, it);
std::cout << distance_between << std::endl;
nsp::test_container<int>::reverse_iterator rit = it, rit2 = it2;
--rit;
++rit;
advance(rit, -2);
distance_between = distance(rit2, rit);
std::cout << distance_between << std::endl;
std::cin.get();
return 0;
}