Specific route with iterator on Standard Container - c++

I have a class with an standard container member, and I'm wondering is that possible that I make an own iterator with a specific route, for example it goes back and forth, and after that stops.
template<class T>
class compressed_string {
vector<T> v;
public:
typedef typename std::vector<T>::iterator iterator;
iterator begin() { return v.begin(); }
iterator end() { return v.end(); }
compressed_string& add(const T& elem) {
v.push_back(elem);
return *this;
}
basic_string<T> not_nice_way_to_make_real_string() {
basic_string<T> tmp;
for(iterator i = v.begin(); i < v.end(); ++i)
tmp += *i;
for(iterator i = --v.end(); i >= v.begin(); --i)
tmp += *i;
return tmp;
}
};
main:
compressed_string<char> s;
s.add('q').add('w').add('e').add('w');
cout << s.not_nice_way_to_make_real_string(); // q w e w w e w q
cout << endl
for ( compressed_string<char>::iterator i = s.begin(); i < s.end(); ++i )
cout << *i;
So with this iterator member the output would be the same in this two lines.
How is this possible?

You'll need an iterator that stores a bit of state:
where it is (e.g. an iterator v_it over v)
where it's going (e.g. bool forward)
where it's bounded (e.g. iterators v_begin = v.begin() and v_end = v.end())
and some otherwise invalid iterator to represent the end, such as {v_end, backward}).
Then implement the increment operator along the lines of:
if (forward) {
if (++v_it == v_end) {
forward = false;
--v_it;
}
} else {
if (v_it-- == v_begin) {
v_it = v_end;
}
}
and similarly for decrement, if you want a bidirectional iterator; in which case, it would be polite to provide a reverse_iterator too. You should provide both pre- and post-increment forms.
You'll also need == and != comparisons, comparing both v_it and forward, and dereference operators * and -> that dereference v_it, and suitable begin and end functions; for bonus points, a const_iterator would be nice.
Note that you'll need random access if you really want the code in your question (i < s.end() rather than the more generic i != s.end()) to work; that's entirely possible, but rather excessive if you don't otherwise need it.
UPDATE: as noted in the comments, this particular implementation could probably be improved a bit; for example, it's possible to remove the need to store v_begin if you're a bit careful about how you define the end iterator.

Related

How to compare iterator value with an integer?

The function removeAll(vector<int>& v, const int& x) intends to remove all elements that equals to int x except the first one.
For example
originally
v = [2,2,3,5,5,6,2,8,6]
after removeAll(v, 2)
the output should be [2,3,5,5,6,8,6]
But my code seems fail to compare if (*it == x), so does anyone know the reason? I didn't find any similar question online.
void removeAll(vector<int>& v, const int& x)
{
for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
{
int count = 0;
if (*it == x && count > 0)
{
v.erase(it);
}
if (*it == x)
{
count++;
}
}
}
What you want is this:
void remove_duplicates_of(std::vector<int>& v, int value)
{
auto it = std::find(v.begin(), v.end(), value);
if (it != v.end())
v.erase(std::remove(std::next(it), v.end(), value), v.end());
}
This finds the first instance of value, and if found, erases every subsequent instance starting at the iterator position immediately past the point of discovery.
There are several problems:
count must be declared outside of the loop, otherwise it's reset to 0 on every iteration.
it must be incremented only when you chose to not erase an element.
When you do erase an element, the return value of erase should be assigned to it. (Failing to do so would formally cause UB, but in practice might not break anything, if you're using std::vector. It's not going to work with for most other containers though.)
Here's the fixed code:
void removeAll(vector<int>& v, const int& x)
{
int count = 0;
for (vector<int>::iterator it = v.begin(); it < v.end();)
{
if (*it == x)
{
count++;
}
if (*it == x && count > 1)
{
it = v.erase(it);
}
else
{
it++;
}
}
}
And here's the same code with minor style improvements:
void removeAll(std::vector<int> &v, int x)
{
std::size_t count = 0;
for (auto it = v.begin(); it < v.end();)
{
if (*it == x && count++ != 0)
it = v.erase(it);
else
it++;
}
}
When you erase an iterator, it gets invalidated. The best way of doing this is with standard algorithms as shown in another answer. The thing is, removing elements from the middle of a vector is expensive. It shifts all the elements after that position by 1 element every time you call it. It's better to shift all the elements in a single iteration and then remove what remains at the end. Removing elements from the end is relatively cheap. Here's roughly how you can do it without standard algorithms:
void remove_duplicates_of(std::vector<int>& v, int x) {
auto it = v.begin();
auto last = v.end();
while (it != last && *it != x) ++it; // find the first one
if (it == last) return; // if we didn't find anything, return
++it; // skip the first
while (it != last && *it != x) ++it; // find the second
if (it == last) return; // if we didn't find anything, return
auto next = it;
while (++it != last) // shift all other elements to front
if (*it != x) *next++ = *it;
// remove the rest
v.erase(next, last);
}

Iterating through a vector for comparison in C++

I'm trying to write a program where given a vector, you use iterators to compare the first and last number of the vector, then moves in and compares the next ones. I wrote the for loop to do that, but am unsure how to make it stop once they reach the center of the vector.
For the for loop I have:
for (a = v.begin(), b = v.rbegin(); a != v.end(), b != v.rend(); a++, b++)
where a is the forward iterator and b is a backwards iterator.
My assumption is that I need to change the condition of the for loop, but I'm unsure to what.
So bear in mind that std::vector<T>::iterator is a random-access iterator, which means that it has operator< defined.
Using this, and using the std::reverse_iterator<Iterator>::base() member function, we can rewrite your for-loop to the following:
auto a = v.begin();
auto b = v.rbegin();
for (; a < b.base(); ++a, ++b)
{
// Do stuff...
}
First of all you need to use && and not the , operator in the comparison, which doesn't do what you think it does.
For your specific question you just keep going until both iterators reach each other, you can obtain the underlying std::iterator of a std::reverse_iterator through base(), eg:
template<typename T> bool isPalindrome(const std::vector<T>& data)
{
for (auto it = data.begin(), it2 = data.rbegin(); it != data.end() && it2 != data.rend() &&
it != it2.base(); ++it, ++it2)
if (*it != *it2)
return false;
return true;
}

Converting const auto & to iterator

A number of posts I've read lately claim for(const auto &it : vec) is the same as using the longer iterator syntax for(std::vector<Type*>::const_iterator it = vec.begin(); it != vec.end(); it++). But, I came upon this post that says they're not the same.
Currently, I'm trying to erase an element in a for loop, after it is used, and wondering if there is any way to convert const auto &it : nodes to std::vector<txml::XMLElement*>::iterator?
Code in question:
std::vector<txml2::XMLElement *> nodes;
//...
for (const auto &it : nodes)
{
//...
nodes.erase(it);
}
I pretty sure I could just rewrite std::vector<txml2::XMLElement*> as a const pointer, but would prefer not to since this code is just for debugging in the moment.
You should not be attempting to convert the range declaration in your range based for loop to an iterator and then deleting it whilst iterating. Even adjusting iterators while iterating is dangerous, and you should instead rely on algorithms.
You should use the Erase-remove idom.
You can use it with remove_if.
It would look something like:
nodes.erase( std::remove_if(nodes.begin(), nodes.end(), [](auto it){
//decide if the element should be deleted
return true || false;
}), nodes.end() );
Currently in the technical specifications, is erase_if.
This is a cleaner version of the same behaviour shown above:
std::erase_if(nodes,[](auto it){
//decide if the element should be deleted
return true || false;
});
You don't get an iterator but a reference to the element. Unless you want to do a std::find with it, it's pretty hard to get an iterator out of it.
Vectors are nice, so you could increase a counter per element and do nodes.begin() + counter to get the iterator, but it'd sort of defeat the point.
Also erasing the iterator in the for loop will result in you iterating after the end of the vector, you can test this code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {0,1,2,3,4,5,6};
for (int x : v) {
cout << x << endl;
if (x == 2) {
v.erase(v.begin() + 2);
}
}
return 0;
}
If you want to use iterators, just do a loop with them, if in addition you want to erase one mid-loop you have to follow this answer:
for (auto it = res.begin() ; it != res.end(); ) {
const auto &value = *it;
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
Note that you don't need to specify the whole type of the iterator, auto works just as well.

iterator over multiple std containers subsequently

I have the following class (which obviously does not yet work as intended):
class A
{
private:
std::vector<int> firstVector, secondVector;
public:
std::vector<int>::iterator begin(){
return firstVector.begin();
}
std::vector<int>::iterator end(){
return secondVector.end();
}
};
How can I define an iterator which will go over the two member containers subsequently, e.g. after firstVector.end()-1 secondVector.begin() is returned and going all the way to secondVector.end() ?
Basically you need to define some custom iterator that internally checks for the end of the first range, then goes on to the next one.
However, this sort of stuff occurs a lot. Eventually you'd ask why an iterator for two vectors, why an iterator for two vectors, why a sequence of the same container type, and so on. Nir Tzachar & I have written a C++ port of Python itertools that does this sort of common stuff. In this case, you'd just use
chain(firstVector, secondVector)
It can be downloaded from this bitbucket repo.
Nothing to stop you from rolling your own. Can even make it random access!
struct chain_iterator
: std::iterator<std::random_access_iterator_tag, int>
{
using it = std::vector<int>::iterator;
std::pair<it, it> v1, v2;
bool first;
it cur;
};
We keep the initial iterator pairs so that we can do random access correctly.
Incrementing is what you'd expect:
chain_iterator& operator++() {
++cur;
if (first && cur == v1.second) {
first = false;
cur = v2.first;
}
return *this;
}
Dereference is trivial:
int& operator*() { return *cur; }
Advance has to do some extra checking:
chain_iterator& operator+=(size_t n) {
if (!first) {
// trivial case
cur += n;
}
else {
size_t d = v1.second - cur;
if (d < n) {
cur += n;
}
else {
first = false;
cur = v2.first + (d - n);
}
}
return *this;
}
I'll leave the rest of the operations as an exercise.
You could write your own function to do the incrementing:
std::vector<int>::iterator& inc(std::vector<int>::iterator& it) {
++it;
if (it == firstVector.end())
it = secondVector.begin();
return it;
}
This is also a good indication to others that the increment doesn't happen normally.

STL containers: iterating between two iterators

I am storing values in a std::map
I am finding two values in the map, and I want to iterate between the first through to the last item - however the <= operator is not implemented, so I can't do somethimng like this:
void foobar(const DatedRecordset& recs, const double startstamp, const double endtstamp)
{
DatedRecordsetConstIter start_iter = recs.lower_bound(startstamp), end_iter = recs.lower_bound(endtstamp);
// Can't do this .... (<= not defined)
//for (DatedRecordsetConstIter cit = start_iter; cit <= end_iter; cit++ )
/ So have to resort to a hack like this:
for (DatedRecordsetConstIter cit = start_iter; cit != recs.end(); cit++ ) {
if ((*cit).first <= (*end_iter).first){
//do something;
}
else
break;
}
}
}
Is there a more elegant way of iterating between two known iterators?
Use != instead of <= and it will do what you want it to do.
void foobar(const DatedRecordset& recs, const double startstamp, const double endtstamp)
{
DatedRecordsetConstIter start_iter = recs.lower_bound(startstamp),
end_iter = recs.upper_bound(endtstamp);
for (DatedRecordsetConstIter cit = start_iter; cit != end_iter; ++cit) {
}
}
There isn't a <= operator for std::map<>::iterator, but using != on end_iter should do basically the same thing. If you want to include the end iterator itself in the iteration, use something like a do loop to do the != test at the end.
struct ManipulateMatchingPairs {
template<class K, class V>
void operator()(const std::pair<K,V>& p) const {
// do with p.second as you please here.
}
};
// ...
std::for_each(start_iter, end_iter, ManipulateMatchingPairs());
You have to use the != operator. I believe this is because a std::map isn't necessarily contiguous in memory (so the <= operator wouldn't make much sense, whereas a std::vector would), I could be wrong though
The STL for_each algorithm also will not include the ending iterator in the loop. You could always increcment end_iter and just use for_each so that it will be included, though.
void foobar(const DatedRecordset& recs,
const double startstamp,
const double endtstamp)
{
DatedRecordsetConstIter start_iter = recs.lower_bound(startstamp);
DatedRecordsetConstIter end_iter = recs.lower_bound(endtstamp);
if(end_iter != recs.end())
++end_iter;
for_each(start_iter, end_iter, []()
{
//do something inside the lambda.
});
}
Something like that maybe? I didn't give it a compile check ...
If you want to include the end iterator in the loop, you can increment your end-condition iterator ++end_iter. After that the loop with cit != end_iter does the same as you intend to do with cit <= end_iter before incrementing.