I have a container of container of ints. Lets say deque< deque<int> >. All the numbers in the deque<int> are sorted in ascending order. So the info in the container looks something like this:
deque<deque<int>> // main container
5 11 22 44 // deque<int> number 1
1 7 12 // deque<int> number 2
4 5 7 9 1112 // deque<int> number 3
I want to make an iterator that traverses thru all the numbers in deque<deque<int>> in ascending order. Meaning my iterator can jump between the containers and after I've gone through them all I will have traversed thru the numbers in this order: 1 4 5 5 7 7 9 11 12 22 44 1112. How can I accomplish this? I thought of putting all the numbers in one container and sorting them, but later if I want to say *it = 10 I won't be able because I don't know to which containers position is it pointing. So any ideas and solutions are welcomed :)
Let's focus on a const iterator. Such an iterator should hold an iterator to each of the deques, but also to each of their end (why you need this is explained below). But these iterators should be held in a vector instead of a deque, because you will never push or pop from either end of these containers, since the lifetime of such an iterator ends with a modification of your data structure.
So basically, you need the following structure:
struct const_iterator {
vector<deque<int>::const_iterator> iterators;
vector<deque<int>::const_iterator> ends;
//...
};
Now, you need to implement the increment operator (maybe you also want a decrement operator, which can be implemented analogously), as well as the dereferencing operator.
The increment operator needs to find the deque iterator which currently points to the smallest element, and increment that one.
The dereferencing operator also needs to find the deque iterator which currently points to the smallest element, and dereference that one.
If you're looking for the currently smallest element, ignore the deques which already point to their end. For this, you need all the deque's end iterators. It might be helpful to remember the currently smallest element in another member variable, such that dereferencing becomes a constant time operation. We store that current iterator as an iterator into the vector of iterators, such that we can increment it (so the iterator in the vector is changed).
struct nested_deque_iterator {
vector<deque<int>::const_iterator> iterators;
vector<deque<int>::const_iterator> ends;
vector<deque<int>::const_iterator>::iterator current;
bool at_end = false;
};
Updating the smallest element could be implemented in a helper function, which modifies the member variable current as well as at_end. This needs to be called in the constructor for a correct initialization of the member variable:
void update_current() {
if (!at_end && iterators.size() > 0) {
// At the beginning, we assume that we didn't find any
// new "next smaller element" in any deque.
at_end = true;
// Iterate through the deques (with two iterators in parallel)
for (auto i = iterators.begin(), e = ends.begin();
i != iterators.end() && e != ends.end();
++i, ++e)
{
// We ignore deques which are already at their end
if (i != e) {
// If we found a smaller next element (or the first try)...
if (at_end || *i < *next) {
// ... then replace the current iterator with it:
at_end = false;
current = i;
}
}
}
}
}
Then, dereferencing becomes as easy as dereferencing the current iterator twice (since it is an iterator into the vector of iterators... I know it is a bit confusing...)
int operator *() const {
return **current;
}
And incrementing will increment the (dereferenced) current iterator, as well as call the helper function to update it (this is the pre-increment operator):
nested_deque_iterator& operator++() {
if (!at_end) {
++(*current);
update_current();
}
}
You can implement the post-increment operator by means of pre-incrementing:
nested_deque_iterator operator++(int) {
nested_deque_iterator old(*this);
++(*this);
return old;
}
We also need the equality operator to compare iterators with each other:
bool operator==(const nested_deque_iterator &o) const {
// If either iterator is at the end, don't dereference current!
if (at_end || o.at_end) {
return at_end == o.at_end;
}
return *current == *(o.current);
}
And finally the inequality operator by means of equality:
bool operator!=(const nested_deque_iterator &o) const {
return !(*this == o);
}
Finally, write a begin and end construction helper function for your nested deques.
nested_deque_iterator nested_deque_begin(const deque<deque<int>> & deques) {
vector<deque<int>::const_iterator> iterators;
vector<deque<int>::const_iterator> ends;
for (const auto & d : deques) {
iterators.push_back(d.begin());
ends.push_back(d.end());
}
return { iterators, ends };
}
nested_deque_iterator nested_deque_end(const deque<deque<int>> & deques) {
vector<deque<int>::const_iterator> ends;
for (const auto & d : deques) {
ends.push_back(d.end());
}
return { ends, ends };
}
And if you want to, also a container adaptor (if you don't already have such) which uses these construction helper functions as their default begin and end methods:
struct nested_deque {
deque<deque<int>> deques;
//...
}
nested_deque_iterator begin(const nested_deque & nd) {
return nested_deque_begin(nd.deques);
}
nested_deque_iterator end(const nested_deque & nd) {
return nested_deque_end(nd.deques);
}
So you can write:
deque<deque<int>> mydeque = ...;
for (int i : nested_deque{mydeque}) {
// Here you go.
}
The full implementation is available here.
Implement your own iterator:
struct sorted_iterator {
int& operator*() { return *(*this->it); }
sorted_iterator& operator++() { ++(this->it); return *this;}
bool operator==( const sorted_iterator& other ) const;
bool operator!=( const sorted_iterator& other ) const;
private:
std::vector<int*> sorted;
decltype(sorted)::iterator it;
};
and in it, internally, build a vector of int*
std::vector<int*> sorted;
for( std::deque<int> x : main_container )
for( int& i : x )
sorted.push_back(&i);
auto compare = []( int* i, int* j ) { return *i < *j; }
std::sort( sorted.begin(), sorted.end(), compare );
Related
Suppose I have a vector named spot_deals of SpotDeal that is a class:
class SpotDeal
{
public:
int deal_id_; // primary key, and vector is sorted by id
string ccy_pair_; // ccy pair, e.g. GBPUSD, AUDUSD
double amount_;
}
Say I need to pass two subset of spot_deals to a function foo for some computation. I could make copies, however, that would cost memory and time. Actually foo only needs iterators of deals. So can I make 2 iterators of vector<SpotDeal>, namely it1 and it2 and pass them to foo?
The two subset of spot_deals could be filtered by ccy_pair_, e.g. deals of GBPUSD and AUDUSD, or by other conditions. So I'm looking for a way to define an iterator defined by a vector and a lambda function (could equivalently be a functor though).
Is there a way to write a helper function make_filtered_iterator so that I can have something like below?
auto it1 = make_filtered_iterator(spot_deals, filter_lambda1);
auto it2 = make_filtered_iterator(spot_deals, filter_lambda2);
foo(it1, it2);
The answer is certainly "yes." C++ iterators in the STL style can be made to do all sorts of tricks. A common but basic one is making an iterator for std::map which when dereferenced gives only the key or the value.
In your particular case, a simple implementation might be like this:
template <typename BaseIterator>
struct filtered_iterator : BaseIterator
{
typedef std::function<bool (const value_type&)> filter_type;
filtered_iterator() = default;
filtered_iterator(filter_type filter, BaseIterator base, BaseIterator end = {})
: BaseIterator(base), _end(end), _filter(filter_type) {
while (*this != _end && !_filter(**this)) {
++*this;
}
}
filtered_iterator& operator++() {
do {
BaseIterator::operator++();
} while (*this != _end && !_filter(**this));
}
filtered_iterator operator++(int) {
filtered_iterator copy = *this;
++*this;
return copy;
}
private:
BaseIterator _end;
filter_type _filter;
};
template <typename BaseIterator>
filtered_iterator<BaseIterator> make_filtered_iterator(
typename filtered_iterator<BaseIterator>::filter_type filter,
BaseIterator base, BaseIterator end = {}) {
return {filter, base, end};
}
I set a default value for end because typically you can use a default-constructed iterator for that. But in some cases you might want to filter only a subset of the container, in which case specifying the end makes it easy.
Yes, it would be possible to create an iterator type. However, I suspect your question is an example of an XY problem (https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) - you want to find a way to operate differently on two subsets of a vector (X), have decided the solution must involve implementing a special-purpose iterator (Y), and have asked how to do Y rather than X. I am going to provide an option to do X without needing to do Y.
I suggest it would be easier to use standard algorithm std::stable_partition() to separate the container into two ranges.
auto false_partition = std::stable_partition(your_vector.begin(), your_vector.end(), your_filter);
The begin() and end() iterators of the vector do not change (i.e. are not invalidated), but the elements between them are reorganised into two ranges, such that the elements for which your_filter returns true precedes the set of elements for which your_filter returns false. false_partition is therefore simultaneously the "past the end" iterator for the first range, and the beginning of the second range. The order of elements in each range is the same as in the original vector.
These can be used as follows
// a loop to operates on the elements for which your_filter returned true
for (auto i = your_vector.begin(); i != false_partition; ++i)
{
// do whatever
}
// a loop to operates on the elements for which your_filter returned false
for (auto i = false_partition; i != your_vector.end(); ++i)
{
// do whatever
}
Before C++11, the auto keyword can be replaced with appropriate iterator types (e.g. std::vector<int>::iterator or std::vector<int>::const_iterator, depending on whether you want the elements to be changed using the iterators).
I may suggest viewers of this question get a crash course with Eric Nibbler's rangev3 because that's the paradigm adopted for C++20 standard lib.
https://github.com/ericniebler/range-v3
How to do your filter iteration:
for (auto element : spot_deals | views::filter([](auto i) { return condition(i); }))
{ //....
}
I wouldn't use iterators pointing to your original vector, because they cannot convey the size of your subsets. (Basically, you would need one more iterator per subset to represent the end of the subsets.) As of C++20, I would work with ranges from the Ranges library, as mentioned in v.oddou's answer. More specifically, for your use case, I would use the range adaptor std::views::filter as follows:
auto gbpusd = [](const auto& sd) { return sd.ccy_pair_ == "GBPUSD"; };
auto audusd = [](const auto& sd) { return sd.ccy_pair_ == "AUDUSD"; };
auto range1 = spot_deals | std::views::filter(gbpusd);
auto range2 = spot_deals | std::views::filter(audusd);
foo(range1, range2);
This solution does not create temporary vectors for the filtered spot deals, because the view adaptors create ranges that don't contain elements. The resulting ranges range1 and range2 are just views over the vector spot_deals, but with customized iteration behaviors.
The declaration of foo() is a bit tricky, because the data types of the ranges are quite complex. I would therefore use the placeholder type auto for the function parameters and thus make foo() a function template:
void foo(auto& r1, auto& r2) {
for (auto const& sd : r1)
std::cout << sd.deal_id_ << std::endl;
for (auto const& sd : r2)
std::cout << sd.amount_ << std::endl;
}
(Alternatively, you could pass spot_deals by reference to foo() and declare the filtered ranges inside foo().)
Code on Wandbox
By chance, I've recently worked on this exact problem. As it turns out, filtering is the most complicated of quite some operations on a container, and also contains the most pitfalls.
template<typename Range, typename Pred>
class filter
{
public:
friend class const_iterator;
class const_iterator : public std::iterator_traits<typename Range::const_iterator>
{
using underlying = typename Range::const_iterator;
public:
auto operator*() {return *u;}
const_iterator& operator++()
{
++u;
normalize();
return *this;
}
const_iterator operator++(int)
{
auto t = *this;
u++;
normalize();
return t;
}
bool operator==(const const_iterator& rhs) const {return u == rhs.u;}
bool operator!=(const const_iterator& rhs) const {return !(*this == rhs);}
private:
friend filter;
const_iterator(underlying u, const filter& f) : u{std::move(u)}, f{f} {normalize();}
void normalize()
{
for(; u != f.r.end() && !f.p(*u); u++);
}
underlying u;
const filter& f;
};
filter(const Range& r, const Pred& p) : r{r}, p{p} {}
auto begin() const {return const_iterator{r.begin(), *this};}
auto end() const {return const_iterator{r.end(), *this};}
private:
const Range& r;
Pred p;
};
We use it as (with c++17 guide)
vector<int> v{1, 2, 3, 4, 5};
auto f = filter(v, [](int x){return x & 1;});
for(auto i : f)
// all i in v that is odd
Let me explain the pitfalls:
The first element might be filtered out, *r.begin() might not be an element in the filtered range. This means the iterator has to be checked on construction.
r.end() might be invalidated without invalidating other iterators, that is to say, saving a copy of r.end() in any iterators to compare with is a logic error.
operator== is not very straightforward, two underlying iterators referring to different elements in the original range might be referring to the same element in the filtered view because filtered out elements doesn't count as an element.
I have some code that enumerates some data, something like this:
int count;
InitDataEnumeration(/* some init params */, &count);
for (int i = 0; i < count; i++)
{
EnumGetData(i, &data);
// process data ...
}
I'd like to convert this code in a form suitable to C++11's range-for.
I was thinking of defining a DataEnumerator wrapper class, whose constructor would call the above InitDataEnumeration() function.
The idea would be to use this wrapper class like this:
DataEnumerator enumerator{/* init params*/};
for (const auto& data : enumerator)
{
// process data ...
}
How could the former int-indexed for loop be refactored in the latter range-based form?
I was thinking of exposing begin() and end() methods from the enumerator wrapper class, but I don't know what kind of iterators they should return, and how to define such iterators.
Note that the iteration process is forward-only.
What you are looking for can be done with boost::irange. It will construct a lazy range of integers in the range [first, last) and you can just drop it right in like you use i in your for loop.
for (int i = 0; i < count; i++)
{
EnumGetData(i, &data);
// process data ...
}
Becomes
for (auto i : boost::irange(0, count))
{
EnumGetData(i, &data);
// process data ...
}
You require an input iterator this example completely copied from http://en.cppreference.com/w/cpp/iterator/iterator :
#include <iostream>
#include <algorithm>
template<long FROM, long TO>
class Range {
public:
// member typedefs provided through inheriting from std::iterator
class iterator: public std::iterator<
std::input_iterator_tag, // iterator_category
long, // value_type
long, // difference_type
const long*, // pointer
long // reference
>{
long num = FROM;
public:
explicit iterator(long _num = 0) : num(_num) {}
iterator& operator++() {num = TO >= FROM ? num + 1: num - 1; return *this;}
iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
bool operator==(iterator other) const {return num == other.num;}
bool operator!=(iterator other) const {return !(*this == other);}
reference operator*() const {return num;}
};
iterator begin() {return iterator(FROM);}
iterator end() {return iterator(TO >= FROM? TO+1 : TO-1);}
};
int main() {
// std::find requires a input iterator
auto range = Range<15, 25>();
auto itr = std::find(range.begin(), range.end(), 18);
std::cout << *itr << '\n'; // 18
// Range::iterator also satisfies range-based for requirements
for(long l : Range<3, 5>()) {
std::cout << l << ' '; // 3 4 5
}
std::cout << '\n';
}
You are right about begin() and end(), whatever they return should supply:
operator++ (prefix only is enough)
operator!=
operator*
All pretty self-explanatory.
Notice that no traits or categories are required, as would be for iterators intended for some standard library algorithms - just bare minimum.
for(auto x : y) Here, y must be an object of a class that has a begin() method and an end() method that each returns an object implementing the concept of an iterator. The iterator must be incrementable (iter++), must be able to accurately determine if it is equal to another iterator of the same kind (via !=) and must de-reference to whatever x needs to be.
This is something you should consider doing if you either A) are bored or otherwise have nothing better to do or B) have a legit need to. While this is not difficult to do, neither is it trivial.
I have a big vector of items that belong to a certain class.
struct item {
int class_id;
//some other data...
};
The same class_id can appear multiple times in the vector, and the vector is constructed once and then sorted by class_id. So all elements of the same class are next to each other in the vector.
I later have to process the items per class, ie. I update all items of the same class but I do not modify any item of a different class. Since I have to do this for all items and the code is trivially parallelizable I wanted to use Microsoft PPL with Concurrency::parallel_for_each(). Therefore I needed an iterator and came up with a forward iterator that returns the range of all items with a certain class_id as proxy object. The proxy is simply a std::pair and the proxy is the iterator's value type.
using item_iterator = std::vector<item>::iterator;
using class_range = std::pair<item_iterator, item_iterator>;
//iterator definition
class per_class_iterator : public std::iterator<std::forward_iterator_tag, class_range> { /* ... */ };
By now I was able to loop over all my classes and update the items like this.
std::vector<item> items;
//per_class_* returns a per_class_iterator
std::for_each(items.per_class_begin(), items.per_class_end(),
[](class_range r)
{
//do something for all items in r
std::for_each(r.first, r.second, /* some work */);
});
When replacing std::for_each with Concurrency::parallel_for_each the code crashed. After debugging I found the problem to be the following code in _Parallel_for_each_helper in ppl.h at line 2772 ff.
// Add a batch of work items to this functor's array
for (unsigned int _Index=0; (_Index < _Size) && (_First != _Last); _Index++)
{
_M_element[_M_len++] = &(*_First++);
}
It uses postincrement (so a temporary iterator is returned), dereferences that temporary iterator and takes the address of the dereferenced item. This only works if the item returned by dereferencing a temporary object survives, ie. basically if it points directly into the container. So fixing this is easy, albeit the per class std::for_each work loop has to be replaced with a for-loop.
//it := iterator somewhere into the vector of items (item_iterator)
for(const auto cur_class = it->class_id; cur_class == it->class_id; ++it)
{
/* some work */
}
My question is if returning proxy objects the way I did is violating the standard or if the assumption that every iterator dereferences into permanent data has been made by Microsoft for their library, but is not documented. At least I could not find any documentation on the iterator requirements for parallel_for_each() except that either a random access or a forward iterator are expected. I have seen the question about forward iterators and vector but since my iterator's reference type is const value_type& I still think my iterator is ok by the standard. So is a forward iterator returning a proxy object still a valid forward iterator? Or put another way, is it ok for an iterator to have a value type different from a type that is actually stored somewhere in a container?
Compilable example:
#include <vector>
#include <utility>
#include <cassert>
#include <iterator>
#include <memory>
#include <algorithm>
#include <iostream>
#include <ppl.h>
using identifier = int;
struct item
{
identifier class_id;
// other data members
// ...
bool operator<(const item &rhs) const
{
return class_id < rhs.class_id;
}
bool operator==(const item &rhs) const
{
return class_id == rhs.class_id;
}
//inverse operators omitted
};
using container = std::vector<item>;
using item_iterator = typename container::iterator;
using class_range = std::pair<item_iterator, item_iterator>;
class per_class_iterator : public std::iterator<std::forward_iterator_tag, class_range>
{
public:
per_class_iterator() = default;
per_class_iterator(const per_class_iterator&) = default;
per_class_iterator& operator=(const per_class_iterator&) = default;
explicit per_class_iterator(container &data) :
data_(std::addressof(data)),
class_(equal_range(data_->front())), //this would crash for an empty container. assume it's not.
next_(class_.second)
{
assert(!data_->empty()); //a little late here
assert(std::is_sorted(std::cbegin(*data_), std::cend(*data_)));
}
reference operator*()
{
//if data_ is unset the iterator is an end iterator. dereferencing end iterators is bad.
assert(data_ != nullptr);
return class_;
}
per_class_iterator& operator++()
{
assert(data_ != nullptr);
//if we are at the end of our data
if(next_ == data_->end())
{
//reset the data pointer, ie. make iterator an end iterator
data_ = nullptr;
}
else
{
//set to the class of the next element
class_ = equal_range(*next_);
//and update the next_ iterator
next_ = class_.second;
}
return *this;
}
per_class_iterator operator++(int)
{
per_class_iterator tmp{*this};
++(*this);
return tmp;
}
bool operator!=(const per_class_iterator &rhs) const noexcept
{
return (data_ != rhs.data_) ||
(data_ != nullptr && rhs.data_ != nullptr && next_ != rhs.next_);
}
bool operator==(const per_class_iterator &rhs) const noexcept
{
return !(*this != rhs);
}
private:
class_range equal_range(const item &i) const
{
return std::equal_range(data_->begin(), data_->end(), i);
}
container* data_ = nullptr;
class_range class_;
item_iterator next_;
};
per_class_iterator per_class_begin(container &c)
{
return per_class_iterator{c};
}
per_class_iterator per_class_end()
{
return per_class_iterator{};
}
int main()
{
std::vector<item> items;
items.push_back({1});
items.push_back({1});
items.push_back({3});
items.push_back({3});
items.push_back({3});
items.push_back({5});
//items are already sorted
//#define USE_PPL
#ifdef USE_PPL
Concurrency::parallel_for_each(per_class_begin(items), per_class_end(),
#else
std::for_each(per_class_begin(items), per_class_end(),
#endif
[](class_range r)
{
//this loop *cannot* be parallelized trivially
std::for_each(r.first, r.second,
[](item &i)
{
//update item (by evaluating all other items of the same class) ...
//building big temporary data structure for all items of same class ...
//i.processed = true;
std::cout << "item: " << i.class_id << '\n';
});
});
return 0;
}
When you're writing a proxy iterator, the reference type should be a class type, precisely because it can outlive the iterator it is derived from. So, for a proxy iterator, when instantiating the std::iterator base should specify the Reference template parameter as a class type, typically the same as the value type:
class per_class_iterator : public std::iterator<
std::forward_iterator_tag, class_range, std::ptrdiff_t, class_range*, class_range>
~~~~~~~~~~~
Unfortunately, PPL is not keen on proxy iterators and will break compilation:
ppl.h(2775): error C2338: lvalue required for forward iterator operator *
ppl.h(2772): note: while compiling class template member function 'Concurrency::_Parallel_for_each_helper<_Forward_iterator,_Function,1024>::_Parallel_for_each_helper(_Forward_iterator &,const _Forward_iterator &,const _Function &)'
with
[
_Forward_iterator=per_class_iterator,
_Function=main::<lambda_051d98a8248e9970abb917607d5bafc6>
]
This is actually a static_assert:
static_assert(std::is_lvalue_reference<decltype(*_First)>::value, "lvalue required for forward iterator operator *");
This is because the enclosing class _Parallel_for_each_helper stores an array of pointers and expects to be able to indirect them later:
typename std::iterator_traits<_Forward_iterator>::pointer _M_element[_Size];
Since PPL doesn't check that pointer is actually a pointer, we can exploit this by supplying a proxy pointer with an operator* and overloading class_range::operator&:
struct class_range_ptr;
struct class_range : std::pair<item_iterator, item_iterator> {
using std::pair<item_iterator, item_iterator>::pair;
class_range_ptr operator&();
};
struct class_range_ptr {
class_range range;
class_range& operator*() { return range; }
class_range const& operator*() const { return range; }
};
inline class_range_ptr class_range::operator&() { return{*this}; }
class per_class_iterator : public std::iterator<
std::forward_iterator_tag, class_range, std::ptrdiff_t, class_range_ptr, class_range&>
{
// ...
This works great:
item: item: 5
1
item: 3item: 1
item: 3
item: 3
Press any key to continue . . .
For your direct question, no, iterator does not have to be something which is related to any kind of container. About only requirements for an iterator are for it to be:
be copy-constructible, copy-assignable and destructible
support equality/inequality
be dereferencable
Iterator does not necessarily has to be tied to a particular container (see generators), and so it cannot be said that "it has to has same type as container" - because there is no container in generic case.
It seems, hovever, having a custom iterator class may be actually an overkill in your case. Here's why:
In C++, array/vector end iterator is and iterator pointing just behind the end of the last item.
Given a vector of objects of "classes" (in your definition) A,B,C, etc., filled like following:
AAAAAAABBBBBBBBBBBBCCCCCCCD.......
You can just take regular vector iterators that will act as your range starts and ends:
AAAAAAABBBBBBBBBBBBCCCCCCCD......Z
^ ^ ^ ^ ^
i1 i2 i3 i4 iN
For the 4 iterators you see here, following is true:
i1 is begin iterator for class A
i2 is end iterator for class A and begin iterator for class B
i3 is end iterator for class B and begin iterator for class C etc.
Hence, for each class you can have a pair of iterators which are start and end of the respective class range.
Hence, your processing is as trivial as:
for(auto it = i1; i!= i2; i++) processA(*it);
for(auto it = i2; i!= i3; i++) processB(*it);
for(auto it = i3; i!= i4; i++) processC(*it);
Each loop being trivially parallelizable.
parallel_for_each (i1; i2; processA);
parallel_for_each (i2; i3; processB);
parallel_for_each (i3; i4; processC);
To use a range-based for, you can introduce a substitute range class:
class vector_range<T> {
public:
vector<T>::const_iterator begin() {return _begin;};
vector<T>::const_iterator end() {return _end;};
// Trivial constructor filling _begin and _end fields
}
That is to say, you don't really need a proxy iterators to parallelize loops - the way C++ iterators are done is already ideally covers your case.
I have this Container class with begin() and end() functions for use with c++11 foreach loops:
class Element
{
//content doesn't matter
};
class Container
{
Element* elements;
int size;
/* constructor, destructor, operators, methods, etc.. */
Element* begin() { return elements; };
Element* end() { return elements + size; };
};
This is now a valid c++11 foreach loop:
Container container;
for (Element& e : container)
{
//do something
}
But now consider this foreach loop:
Container container;
for (Element* e : container)
{
//do something
}
Is it possible to have a foreach loop with Element* instead of Element& like this?
This would also have the great advantage of preventing one from typing for (Element e : container) which would copy the element each time.
In that case begin() and end() would have to return Element** as far as I know.
But sizeof(Element) is not guaranteed to be sizeof(Element*) and in most cases they don't match. Incrementing a pointer increments by the base type size which is sizeof(Element) for incrementing Element* and sizeof(Element*) for incrementing Element**.
So the prefix operator++() will offset the pointer by a false value and things get crappy. Any ideas how to get this to work?
I agree with LRiO that what you have right now is probably the best solution. It additionally lines up with how the standard containers operate, and taking the path of least surprise for your users is always the best path to take (barring compelling reasons to diverge).
That said, you can certainly get the behavior you want:
class Container
{
// ...
struct iterator {
Element* e;
// this is the important one
Element* operator*() { return e; }
// the rest are just boilerplate
iterator& operator++() { ++e; return *this; }
iterator operator++(int) {
iterator tmp{e};
++*this;
return tmp;
}
bool operator==(iterator rhs) const { return e == rhs.e; }
bool operator!=(iterator rhs) const { return e != rhs.e; }
};
iterator begin() { return {elements}; };
iterator end() { return {elements + size}; };
};
You could consider inheriting from std::iterator to get the typedefs right, or using boost::iterator_facade. But this'll at least give you the functionality.
I can see why you'd want to do this, since an Element*→Element typo would be caught as an error right away, whereas an Element&→Element typo is usually a silent bug. However, in the grand scheme of things, I don't think it's worth transforming your entire container.
You could try to create a container adaptor that maintains the current behaviour of its iterators, except in the value type they expose… but I'm not even sure it's possible to do that without breaking various preconditions.
Personally, I just wouldn't.
I have a vector< Object > myvec which I use in my code to hold a list of objects in memory. I keep a pointer to the current object in that vector in the "normal" C fashion by using
Object* pObj = &myvec[index];
This all works fine if... myvec doesn't grow big enough that it is moved around during a push_back at which time pObj becomes invalid - vectors guarantee data is sequential, hence they make no effort to keep the vector at the same memory location.
I can reserve enough space for myvec to prevent this, but I dnt' like that solution.
I could keep the index of the selected myvec position and when I need to use it just access it directly, but it's a costly modification to my code.
I'm wondering if iterators keep the their references intact as a vector is reallocated/moved and if so can I just replace
Object* pObj = &myvec[index];
by something like
vector<Object>::iterator = myvec.begin()+index;
What are the implication of this?
Is this doable?
What is the standard pattern to save pointers to vector positions?
Cheers
No... using an iterator you would have the same exact problem. If a vector reallocation is performed then all iterators are invalidated and using them is Undefined Behavior.
The only solution that is reallocation-resistant with an std::vector is using the integer index.
Using for example std::list things are different, but also the are different efficiency compromises, so it really depends on what you need to do.
Another option would be to create your own "smart index" class, that stores a reference to the vector and the index. This way you could keep just passing around one "pointer" (and you could implement pointer semantic for it) but the code wouldn't suffer from reallocation risks.
Iterators are (potentially) invalidated by anything that could resize the vector (e.g., push_back).
You could, however, create your own iterator class that stored the vector and an index, which would be stable across operations that resized the vector:
#include <iterator>
#include <algorithm>
#include <iostream>
#include <vector>
namespace stable {
template <class T, class Dist=ptrdiff_t, class Ptr = T*, class Ref = T&>
class iterator : public std::iterator<std::random_access_iterator_tag, T, Dist, Ptr, Ref>
{
T &container_;
size_t index_;
public:
iterator(T &container, size_t index) : container_(container), index_(index) {}
iterator operator++() { ++index_; return *this; }
iterator operator++(int) { iterator temp(*this); ++index_; return temp; }
iterator operator--() { --index_; return *this; }
iterator operator--(int) { stable_itertor temp(*this); --index_; return temp; }
iterator operator+(Dist offset) { return iterator(container_, index_ + offset); }
iterator operator-(Dist offset) { return iterator(container_, index_ - offset); }
bool operator!=(iterator const &other) const { return index_ != other.index_; }
bool operator==(iterator const &other) const { return index_ == other.index_; }
bool operator<(iterator const &other) const { return index_ < other.index_; }
bool operator>(iterator const &other) const { return index_ > other.index_; }
typename T::value_type &operator *() { return container_[index_]; }
typename T::value_type &operator[](size_t index) { return container_[index_ + index]; }
};
template <class T>
iterator<T> begin(T &container) { return iterator<T>(container, 0); }
template <class T>
iterator<T> end(T &container) { return iterator<T>(container, container.size()); }
}
#ifdef TEST
int main() {
std::vector<int> data;
// add some data to the container:
for (int i=0; i<10; i++)
data.push_back(i);
// get iterators to the beginning/end:
stable::iterator<std::vector<int> > b = stable::begin(data);
stable::iterator<std::vector<int> > e = stable::end(data);
// add enough more data that the container will (probably) be resized:
for (int i=10; i<10000; i++)
data.push_back(i);
// Use the previously-obtained iterators:
std::copy(b, e, std::ostream_iterator<int>(std::cout, "\n"));
// These iterators also support most pointer-like operations:
std::cout << *(b+125) << "\n";
std::cout << b[150] << "\n";
return 0;
}
#endif
Since we can't embed this as a nested class inside of the container like a normal iterator class, this requires a slightly different syntax to declare/define an object of this type; instead of the usual std::vector<int>::iterator whatever;, we have to use stable::iterator<std::vector<int> > whatever;. Likewise, to obtain the beginning of a container, we use stable::begin(container).
There is one point that may be a bit surprising (at least at first): when you obtain a stable::end(container), that gets you the end of the container at that time. As shown in the test code above, if you later add more items to the container, the iterator your obtained previously is not adjusted to reflect the new end of the container -- it retains the position it had when you obtained it (i.e., the position that was the end of the container at that time, but isn't any more).
No, iterators are invalidated after vector growth.
The way to get around this problem is to keep the index to the item, not a pointer or iterator to it. This is because the item stays at its index, even if the vector grows, assuming of course that you don't insert any items before it (thus changing its index).