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.
Related
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.
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;
}
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));
}
I have a vector that looks like this:
std::vector<std::vector<MyClass>> myVector;
And I would like to access its elements through iterators as if it was an unidimensional vector:
for (auto& x : myVector)
{
foo(x); // x is an object of type MyClass
}
(i.e. the fact that there are multiple dimensions is transparent to whoever loops through myVector)
I have an idea of how this should be done, have a custom iterator implementation that saves current indexes so that when one of the vectors has no more elements, it resets one of the indexes and increments the other so that it can start iterating through the next vector and so on. But I have been trying to code this idea but can't seem to get this working. Does anyone have any idea of how I can possibly achieve this? Or even better, if there's any open-source project that has a similar implementation?
Thanks.
It's totally possible to define your own iterator to hide all the details of iterating through a vector of vector, I wrote some code to give you the idea, mind that it should require more checks but it basically works and give you the idea.
You just need to write the required operations to make it work in other code like an opaque iterator.
template <typename T>
struct vector2d
{
public:
class iterator
{
public:
using vector_type = std::vector<std::vector<T>>;
using first_level_iterator = typename std::vector<std::vector<T>>::iterator;
using second_level_iterator = typename std::vector<T>::iterator;
private:
vector_type& data;
first_level_iterator fit;
second_level_iterator sit;
public:
iterator(vector_type& data, bool begin) : data(data)
{
if (begin)
{
fit = data.begin();
sit = fit->begin();
}
else
{
fit = data.end();
}
}
inline bool operator!=(const iterator& other) const { return fit != other.fit || (fit != data.end() && sit != other.sit); }
inline const iterator& operator++() {
// don't go past end
if (fit == data.end())
return *this;
// increment inner iterator
++sit;
// if we reached the end of inner vector
if (sit == fit->end())
{
// go to next vector
++fit;
// if we reached end then don't reset sit since it would be UB
if (fit != data.end())
sit = fit->begin();
}
return *this;
}
T& operator*() const { return *sit; }
};
public:
std::vector<std::vector<T>> data;
iterator begin() { return iterator(this->data, true); }
iterator end() { return iterator(this->data, false); }
};
A small test:
int main() {
vector2d<int> data;
data.data.push_back(vector<int>());
data.data.push_back(vector<int>());
data.data.push_back(vector<int>());
for (int i = 1; i < 5; ++i)
{
data.data[0].push_back(i);
data.data[1].push_back(i*2);
data.data[2].push_back(i*3);
}
for (auto i : data)
{
cout << i << endl;
}
return 0;
}
The behavior is rather simple but you must make sure that it's always consistent for all the edge cases.
A pretty minimal range type:
template<class It>
struct range_t {
private:
It b, e;
public:
It begin() const { return b; }
It end() const { return e; }
decltype(auto) front() const { return *b; }
decltype(auto) back() const { return *std::prev(e); }
bool empty() const { return b==e; }
range_t without_front( std::size_t n = 1 ) const {
auto r = *this;
std::advance(r.b,n);
return r;
}
range_t without_back( std::size_t n = 1 ) const {
auto r = *this;
std::advance(r.e,std::ptrdiff_t(-n));
return r;
}
range_t(It s, It f):b(std::move(s)), e(std::move(f)) {}
range_t():b(), e() {}
};
template<class It>
range_t<It> range( It b, It e ) {
return {std::move(b), std::move(e)};
}
Doing this task is far easier with ranges than with iterators.
template<class Outer, class Inner>
struct stacked_range_t {
range_t<Outer> outer;
stacked_range_t()=default;
stacked_range_t( range_t<Outer> o ):outer(std::move(o)) {}
struct iterator {
private:
range_t<Outer> outer;
range_t<Inner> inner;
public:
iterator(
range_t<Outer> o,
range_t<Inner> i
):outer(std::move(o)), inner(std::move(i)) {}
iterator()=default;
friend auto mytie(iterator const& it) {
return std::tie( it.outer.begin(), it.inner.begin(), it.inner.end() );
}
friend bool operator==(iterator const& lhs, iterator const& rhs) {
return mytie(lhs)==mytie(rhs);
}
friend bool operator!=(iterator const& lhs, iterator const& rhs) {
return mytie(lhs)==mytie(rhs);
}
using difference_type = std::ptrdiff_t;
using value_type = typename std::iterator_traits<Inner>::value_type;
using pointer = typename std::iterator_traits<Inner>::pointer;
using reference = typename std::iterator_traits<Inner>::reference;
using iterator_category = std::input_iterator_tag;
reference operator*() const {
return *inner.begin();
}
pointer operator->() const {
return inner.begin().operator->();
}
iterator& operator++() {
using std::begin; using std::end;
inner = inner.without_front();
while (inner.empty())
{
outer = outer.without_front();
if (!outer.empty())
inner = range( begin(outer.front()), end(outer.front()) );
}
return *this;
}
iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
};
iterator end() const {
return { range( outer.end(), outer.end() ), {} };
}
// a bit tricky:
iterator begin() const {
if (outer.empty()) return end();
auto rout = outer;
while( !rout.empty() ) {
using std::begin; using std::end;
auto rin = range( begin(rout.front()), end(rout.front()) );
if (!rin.empty())
return {std::move(rout), std::move(rin)};
rout = rout.without_front();
}
return end();
}
};
and a function to create it:
template<class Range>
auto make_stacked_range(Range& r) {
using std::begin; using std::end;
using Outer = decltype(begin(r));
using Inner = decltype(begin(*begin(r));
return stacked_range_t<Outer, Inner>{
{begin(r), end(r)}
};
}
there probably are typos. Use of C++1z features can be worked around with overly annoying decltype expressions and helper traits classes.
Relies on the iterators being trivially constructible, and such trivially constructed iterators are equal.
try to use template recursion,e.g.:
#include <stdio.h>
#include <vector>
template <typename V>
void f(V& v){
for(auto& e : v){
f(e);
}
printf("\n");
}
template <>
void f(int& v){
printf("%d ",v);
}
int main(){
std::vector<int> v1={1,2};
f(v1);
std::vector<std::vector<int> > v2={{3,4},{5,6,7}};
f(v2);
return 0;
};
With range/v3:
for (auto& x : myVector | ranges::view::join)
{
foo(x); // x is an object of type MyClass&
}
Demo
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;
}