I have a class for which internally holds pointers to its own kind in a container as shown below:
class A
{
public:
A(int x) : _data(x){}
A ** begin()
{
return v.empty() ? nullptr : &v[0];
}
A ** end()
{
return v.empty() ? nullptr : &v[0] + v.size();
}
void Display()
{
cout << "Data = " << _data << endl;
}
vector<A *> v;
private:
int _data;
};
Now I want to be able to use the range based for loop on the objects of A and thus have written member begin and end for the same. It works as expected. But if I try to replace the vector with a deque for the reason mentioned here, it crashes when trying to access the last element. If the way in which I have implemented the end() is incorrect, can someone please provide a correct one?
Unlike std::vector, std::deque doesn't guarantee that it stores its elements contiguously. So in general pointer arithmetic will not do the right thing. If you want to use a deque, instead of returning pointers into it, you should return iterators.
If you would actually have the deque as a public member then it might make sense to directly return std::deque<A*>::iterator:
std::deque<A*>::iterator begin()
{
return v.begin();
}
std::deque<A*>::iterator end()
{
return v.end();
}
However, it might (depending on your application) be better to have it as a private member and implementation detail, in which case it would also be better to have A::iterator which could just be a typedef for the iterator into the underlying container type, to avoid inappropriately exposing implementation detail:
typedef std::deque<A*>::iterator iterator;
iterator begin()
{
return v.begin();
}
iterator end()
{
return v.end();
}
Related
I have a created a dynamic container for storing bytes as uint8_t and have implemented an iterator class inside the container class. The iterator traverses through the bytes as expected. But these bytes are formatted with headers so I want to implement another iterator to skip through the byte array based on byte headers. Is it possible to have both iterator classes run when used with for(auto &x: container) based on which class is declared first? or is there some other way to do this?
Current iterator and container
class container {
public:
container() : s(0), c(2), data(new uint8_t[2]) {};
container(size_t reserve) : s(0), c(reserve + 1), data(new uint8_t[reserve + 1]) {};
~container();
// container insert, operators etc...
// iterator class
class Iterator {
public:
uint8_t* ptr;
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = uint8_t;
using pointer = uint8_t*;
using reference = uint8_t&;
Iterator(pointer m_ptr) : ptr(m_ptr) { std::cout << "In iterator 1" << std::endl; }
reference operator*() const;
pointer operator->();
Iterator& operator++();
Iterator operator++(int);
Iterator& operator+=(int const& lhs);
Iterator operator+(int const& lhs);
friend bool operator== (const Iterator& a, const Iterator& b);
friend bool operator!= (const Iterator& a, const Iterator& b);
};
Iterator begin() const;
Iterator end() const;
private:
uint8_t* data;
size_t s;
size_t c;
};
You can't make iteration behave differently based on declaration order. What would that even mean? Somehow, that's like asking for the same class to behave differently based on context or perhaps randomly.
However, what you can do is implement code like this:
for (auto x: my_container.raw()) { ... }
for (auto x: my_container.rows()) { ... }
for (auto x: my_container.columns()) { ... }
for (auto x: my_container.pixels()) { ... }
Note: I'm just guessing that the header you mention is a bitmap header and that you're processing an image file.
BTW: There's a bunch of things wrong with your code that are not catastrophic, but still good opportunities to improve, like e.g. using raw new[] memory allocation which should be a std::vector instead. Consider posting your working (!) code on
codereview.stackexchange.com.
No, range iteration syntax is what it is: a proxy for calling the container's begin() and end(); and then iterating over the returned sequence. That's the only way it works.
C++20 adds the ranges library which can be used to combine two separate iterator sequences together. Until C++20 the most common option in this kind of a situation is to declare a third iterator class that proxies over the combined sequence of the two underlying iterator classes, and then use this proxy iterator class with begin() and end(). The basic approach is fairly straightforward:
begin() returns the third iterator class initialized with the beginning and the ending iterator values of both underlying iterator classes, end() returns the third iterator class initialized with the ending iterator values of both underlying iterator classes for both the proxied, internal beginning and ending iterator values (i.e. begin=end for both of them).
The * and/or the -> operator overloads check if the first iterator class's proxied beginning iterator value already reached its ending value. If not then use it to implement * and ->, otherwise use the second proxied beginning iterator value.
The various forward/increment overloads advance the first iterator class's proxied beginning iterator value. If it's already at its iterator class's ending iterator value then the second iterator class's proxied beginning iterator value gets advanced.
This question already has answers here:
How to make my custom type to work with "range-based for loops"?
(10 answers)
Closed 4 years ago.
i have two objects that look like this:
class Object
{
};
class List
{
public:
std::vector<std::shared_ptr<Object>> list;
};
and i want to be able to create a range-based Loop like this:
for (Object& object : list) {
//...
}
for that i created the begin() and end() members:
class List
{
public:
std::vector<std::shared_ptr<Object>> list;
Object& begin() { return *list.begin()->get(); }
const Object& begin() const { return *list.begin()->get(); }
Object& end() { return *list.end()->get(); }
const Object& end() const { return *list.end()->get(); }
};
but the IDE is now asking me for the operators *, ++ and != and i dont undestand how i am supossed to implement can i get some help with that?
The range-for protocol requires that begin() and end() return a type that conforms to the standard library's Iterator concept. This includes things like being able to dereference the iterator with operator*, and increment it with operator++.
An object reference, as you are returning from your begin(), does not meet these requirements, which is what the compiler is complaining about.
Writing your own iterator is quite tedious, especially if you want to support all the random-access facilities that std::vector's iterators offer. There are libraries that can make this a bit easier though.
A far more straightforward way to do things would be to simply forward the iterators that your list member already gives, i.e.
class List
{
public:
using iterator = std::vector<shared_ptr<Object>>::iterator;
using const_iterator = std::vector<shared_ptr<Object>>::const_iterator;
std::vector<std::shared_ptr<Object>> list;
iterator begin() { return list.begin(); }
const_iterator begin() const { return list.begin(); }
iterator end() { return list.end(); }
const_iterator end() const { return list.end(); }
};
Now you can use a List in a range-for loop by saying
for (auto& optr : list) {
do_something_with_object(*optr);
}
The only catch is that dereferencing the iterator will give you a reference to a shared_ptr<Object>, not an Object&, so you'll need to dereference this again to actually use the object itself (as above).
Range-based for loops need more than some arbitrary objects to be returned by begin() and end().
for (auto& object : list) { ... }
for your list is per definition more or less equivalent to the following:
{
auto iterator = list.begin();
auto end = list.end();
for ( ; iterator != end; ++iterator)
{
auto& object = *iterator;
...
}
}
That is why your compiler asks you for all these operations (!=, ++, *), as it tries to apply them to the object returned by begin().
The problem is that you are expected to provide an iterator, not just the first and last element of your list. You cannot give the compiler two different Objects and expect it to be able to loop through a list of pointers to them (which is not necessarily visible from the loop).
Tristan provided you with some options in his answer. I would add that the ranges library can remove a lot of the tedium from writing your own iterators, but it is most likely overkill for your situation and probably not the easiest to get accustomed with.
If you grasped previous replies, I rewrite them in a short snippet. No need for any typedef:
class List {
public:
std::vector<std::shared_ptr<Object>> list;
auto begin() { return list.begin(); }
auto begin() const { return list.begin(); }
auto end() { return list.end(); }
auto end() const { return list.end(); }
};
I need to implement iterators, and i don't have time to make nice iterator classes, so i have decided to just return pointers. It is something like this
int* begin()
{
return p;
}
But i want them to behave as usual stl iterators
*++begin(); // doesn't work because returned pointer isn't l-value
std::vector<int> vi{ 0, 1 };
*++vi.begin(); // works fine
int* p = begin();
*++p; // works fine as well
How can i do this?
Pointers do meet the iterator requirements prefectly (a pointer meets even the most-specialised Random access iterator requirements). Your problem comes from the fact that in the implementation of the standard library which you're using, the iterators provided by e.g. std::vector support more operations than the iterator requirements require.
In other words, the standard does not guarantee that ++vi.begin() will work for a std::vector iterator vi. It happens to work on your implementation of the standard library, but it's an implementation detail. An iterator which would not support that is still a perfectly valid iterator.
So, to answer your question: if you want a quick stand-in for an iterator which will support all iterator operations, you can certainly use a pointer. If you want a quick stand-in for an iterator which will additionally support all the operations your standard library implementation supports in addition to iterator requirements, you might have to roll out your own class.
A minimal iterator is quite easy to whip up using boost:
#include <boost/iterator/iterator_facade.hpp>
using namespace boost;
struct Int100
{
int arr[100];
struct iterator : iterator_facade<iterator,int,forward_traversal_tag>
{
iterator( int* p = nullptr ) : p(p) {}
void increment() { ++p; }
bool equal(const iterator& other) const { return p == other.p; }
int& dereference() const { return *p; }
int* p;
};
iterator begin() { return {arr}; }
iterator end() { return {arr+100}; }
};
This supports the *++begin() syntax you where looking for.
Is there a way to use boost foreach without defining a const_iterator?
My use-case for this is an iterator for a vector, that can contain invalid elements. The iterator should traverse the vector, and yield only the valid elements. It should also repair the vector in the sense that it should swap each invalid item with the next valid item and resize the vector at the end. For example if -1 represents invalid values the vector [6,-1,-1,9,-1,2] should iterate over 6,9 and 2 and leave the vector as [6,9,2].
I tried implementing this with boost::iterator_facade but I could not think of a way to implement a const_iterator, because the vector can change by removing invalid values and thus cannot be const.
Separation of concerns: the container is responsible for its invariants, the iterators for traversal. If you move the repairing to the container you can separate the logical const from the mutable, hidden parts.
Can you write your iterators the 'dumbest' way possible to disentangle them from the container? For instance storing a numerical index (if it makes sense for your container), then calling a private friend (or more) of the container to access the logical n-th element.
The private friend(s) can then be overloaded on const and may still modify the mutable parts to do the repairing you describe, and then return the element.
An (abridged) example of container supporting random-access (and thus numerical indices for access):
template<typename T>
class vector {
mutable std::vector<std::weak_ptr<T>> data; // notice mutable
T&
fetch(int n);
T const&
fetch(int n) const; // notice const overload
public:
class const_iterator;
friend class const_iterator;
const_iterator
begin() const;
};
template<typename T>
class vector<T>::const_iterator {
int index;
vector<T> const* this_; // notice const
public:
// constructors go here
const_iterator&
operator++()
{ ++index; }
// ...
T const&
operator*() const
{ return this_->fetch(index); } // this will call the const version of fetch
};
// example implementation of the const version of fetch
template<typename T>
T const&
vector<T>::fetch(int n) const
{
auto removed = std::remove_if(data.begin(), data.end(), [](std::weak_ptr<T>& element)
{ return element.expired(); });
// mutate mutable data member in a logically const member
data.erase(data.begin(), removed);
// this assumes that there is no race condition
// bear with me for the sake of understanding the mutable keyword
return *data[n].lock();
}
All forms of "foreach" are specifically for iterating over every element of a container. You are not just iterating over every element of a container. You are modifying the container as you iterate.
So just write a regular for-loop. There's no need for special cleverness or anything.
Here's the code for it:
std::vector<int> it = vec.begin();
for(; it != vec.end;)
{
if(*it < 0)
{
it = vec.erase(it);
continue;
}
else
{
//Do stuff with `it`.
++it;
}
}
See, just a simple loop. No need for fancy iterator facades or other such gimmickry.
Say I have a class Foo, which contains some kind of container, say a vector<Bar *> bars. I want to allow the user to iterate through this container, but I want to be flexible so that I might change to a different container in the future. I'm used to Java, where I could do this
public class Foo
{
List<Bar> bars; // may change to a different collection
// User would use this
public Iterator<Bar> getIter()
{
return bars.iterator(); // can change without user knowing
}
}
C++ iterators are designed to look like raw C++ pointers. How do I get the equivalent functionality? I could do the following, which returns the beginning and end of the collection as an iterator that the user can walk himself.
class Foo
{
vector<Bar *> bars;
public:
// user would use this
std::pair<vector<Bar *>::iterator , vector<Bar *>::iterator > getIter()
{
return std::make_pair(bars.begin(), bars.end());
}
}
It works, but I feel I must be doing something wrong.
Function declaration exposes the fact that I'm using a vector. If I change the implementation, I need to change the function declaration. Not a huge deal but kind of goes against encapsulation.
Instead of returning a Java-like iterator class that can do its own bounds check, I need to return both the .begin() and .end() of the collection. Seems a bit ugly.
Should I write my own iterator class?
You could adapt the vector behaviour and provide the same interface:
class Foo
{
std::vector<Bar *> bars;
public:
typedef std::vector<Bar*>::iterator iterator;
iterator begin() {
return bars.begin();
}
iterator end() {
return bars.end();
}
};
Use Foo::iterator as the iterator type outside of the container.
However, bear in mind that hiding behind the typedef offers less than it seems. You can swap the internal implementation as long as it provides the same guarantees. For example, if you treat Foo::iterator as a random access iterator, then you cannot swap a vector for a list internally at a later date without a comprehensive refactoring because list iterators are not random access.
You could refer to Scott Meyers Effective STL, Item 2: beware the illusion of container independent code for a comprehensive argument as to why it might be a bad idea to assume that you can change the underlying container at any point in future. One of the more serious points is iterator invalidation. Say you treat your iterators as bi-directional, so that you could swap a vector for a list at some point. An insertion in the middle of a vector will invalidate all of its iterators, while the same does not hold for list. In the end, the implementation details will leak, and trying to hide them might be Sisyphus work...
You are looking for type erasure. Basically you want an iterator with vector erased from it. This is roughly what it looks like:
#include <vector>
#include <memory>
#include <iostream>
template<class T>
class Iterator{ //the class that erases the iterator type
//private stuff that the user should not care about
struct Iterator_base{
virtual void increment() = 0;
virtual T &dereference() = 0;
virtual ~Iterator_base() = default;
};
std::unique_ptr<Iterator_base> iter;
template<class Iter>
class Iterator_helper : public Iterator_base{
void increment() override{
++iter;
}
T &dereference() override{
return *iter;
}
Iter iter;
public:
Iterator_helper(const Iter &iter) : iter(iter){}
};
public:
template<class Iter>
Iterator(const Iter &iter) : iter(new Iterator_helper<Iter>(iter)){}
//iterator functions for the user
Iterator &operator ++(){
iter->increment();
return *this;
}
T &operator *(){
return iter->dereference();
}
};
struct Bar{
Bar(int i) : i(i){};
int i;
};
class Foo
{
std::vector<Bar> bars;
public:
Foo(){ //just so we have some elements to point to
bars.emplace_back(1);
bars.emplace_back(2);
}
// user would use this
Iterator<Bar> begin()
{
return bars.begin();
}
};
int main(){
Foo f;
auto it = f.begin();
std::cout << (*it).i << '\n'; //1
++it; //increment
std::cout << (*it).i << '\n'; //2
(*it).i++; //dereferencing
std::cout << (*it).i << '\n'; //3
}
You can now pass any iterator (actually anything) to Iterator that support pre-increment, dereferencing and copy constuction, completely hiding the vector inside. You can even assign Iterators that have a vector::iterator inside to an Iterator that has a list::iterator inside, though that may not be a good thing.
This is a very bare-bone implementation, you would want to also implement operators ++ for post-increment, --, ->, ==, =, <, >, <=, >=, != and possibly []. Once you are done with that you need to duplicate the code into a Const_Iterator. If you don't want to do that yourself consider using boost::type_erasure.
Also note that you are paying for this encapsulation with unnecessary dynamic memory allocations, cache misses, virtual function calls that probably cannot be inlined and triply redundant code (same functions in Iterator, Iteratr_base and Iterator_helper).
vector is still present in the private part of Foo, you can get rid of that with a pimpl, adding another level of indirection.
I feel like this bit of encapsulation is not worth the cost, but your mileage may vary.