Convert rend iterator to end iteterator - c++

This is not a duplicate of how to convert a reverse_iterator to an iterator because I want the result to be different to the normal conversion.
Given only a reverse_iterator that is returned from rend, is it possible to convert this to the corresponding iterator that would be returned from end?
For example
std::vector<int> myvec = {...};
auto rit = myvec.rend();
auto it = MAGIC(rit);
it == myvec.end(); // true
If it is not possible to do this given only the reverse_iterator, what is the minimum information needed to do this? (So I can consider workarounds).

Short answer: No.
An iterator refers to one single point in a container, wihtout actual knowledge of the container itself. The iterators returned by end() and rend() point to different ends of a container, i.e. there might be some, many or no elements between the points they refer to, regardless of the reverse nature of one of the iterators. So, without knowing about the container itself or at least its size, there is no possibility to get from one end of the container to the other, and since iterators don't have that knowledge, there is no possibility to get from rend() to end(), from end() to begin() etc. without additional information.
The minimum needed information is the size of the "gap" between the two points. With that and the normal conversion between reverse and non-reverse iterators it is an easy task:
auto rend = v.rend();
auto begin = rend.base();
assert(begin == v.begin());
auto end = begin + v.size(); //the size is the key!
assert(end == v.end());
But, since you can not obtain the size from the reverse_iterator but only from the container itself, you can easily ask it for end() in the first place.

Expression
myvec.rend().base()
is equivalent to
myvec.begin()
Here is a demonstrative example
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<int>::iterator it = v.rend().base();
std::cout << *it << std::endl;
return 0;
}
The output is
1
Another demonstrative program that shows the relation between std::vector<int>::iterator and std::vector<int>::reverse_iterator (instead of templetae argument int you may use any type T>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
if ( v.begin() == v.rend().base() )
{
std::cout << "v.begin() == v.rend().base()" << std::endl;
}
if ( v.end() == v.rbegin().base() )
{
std::cout << "v.end() == v.rbegin().base()" << std::endl;
}
return 0;
}
The output is
v.begin() == v.rend().base()
v.end() == v.rbegin().base()

Related

Removing duplicate elements in using the function "remove"

I tried to remove duplicate elements from a vector by a function vectorremove, using the function remove from the library of algorithms, but it does not work:
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
void vectorremove(vector<string> v)
{
for (vector<string>::iterator it = v.begin(); it != v.end(); ++it)
{
vector<string>::iterator end = remove(it + 1, v.end(), *it);
v.erase(end, v.end());
}
}
int main()
{
vector<string> vect;
string x;
while (cin >> x)
{
vect.push_back(x);
}
vectorremove(vect);
for (vector<string>::iterator it = vect.begin(); it != vect.end(); ++it)
{
cout << *it << endl;
}
return 0;
}
I wrote this code to test if the function vectorremove works, unfortunately it seems that vectorremove has no impact on the vector. Have I made any mistake in the use of remove in the definition of vectorremove?
The first problem in your code is that you are passing the vector by value and not by reference to vectorremove. You need to change that to
void vectorremove(vector<string>& v);
Then inside your vectorremove function you have another problems. vector::erase can invalidate all iterators, so you should onle remove inside the loop and do the erase after the loop.
void vectorremove(vector<string>& v)
{
vector<string>::iterator end{ v.end() };
for (vector<string>::iterator it = v.begin(); it != end; ++it)
{
end = remove(it + 1, end, *it);
}
v.erase(end, v.end());
}
First you are passing std::vector by value, not by reference. Therefore, any changes you make in vectorremove function won't be visible in main.
Furthermore, std::vector::erase might invalidate iterators so you must not use it inside the loop.
Your code could look like:
void vectorremove(std::vector<std::string>& v) {
auto end{ v.end() };
for (auto it = v.begin(); it != end; ++it)
{
end = std::remove(it + 1, end, *it);
}
v.erase(end, v.end());
}
Note the usage of auto instead of std::vector<std::string>::iterator.
However, STL provides handy functions to achieve what you want. One of them is std::unique which
Eliminates all but the first element from every consecutive group of
equivalent elements from the range [first, last) and returns a
past-the-end iterator for the new logical end of the range.
In order to remove duplicates from the std::vector, you can do something like:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v{ 1, 2, 3, 1, 2, 3, 3, 4, 5, 4, 5, 6, 7 };
std::sort(v.begin(), v.end()); // 1 1 2 2 3 3 3 4 4 5 5 6 7
auto last = std::unique(v.begin(), v.end());
v.erase(last, v.end());
for (auto const i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
Remember that std::unique works as expected only on sorted std::vectors.
You only modify the copy of vector , you have to pass by reference to modify the actual
vector, why you don't use auto instead of std::vector::iterator. and you have to
know that erase invalidates all iterators pointing to the erased element and
beyond the erased element, keep the iterator valid by using erase's return value, also use std::getline inside the loop to store the value from std::cin to include the new line.
Alternatively your can use std::unique removes the duplicate value and works as expected after sorting the elements. and std::unique returns a past the end iterator for the new logical end of the range:-
#include <vector>
#include <algorithm>
#include <string>
std::vector<std::string> removeDuplicate(std::vector<std::string> & v){
std::vector<std::string> vec;
std::sort(std::begin(v), std::end(v));
auto pos = std::unique(std::begin(v), std::end(v));
vec.assign(std::begin(v), pos);
return vec;
}
int main(){
std::vector<std::string> vect{"John", "John", "Paul", "John", "Lucy", "Bob", "Bob"};
auto pureVector = removeDuplicate(vect);
for(auto const & v : pureVector){
std::cout << v << '\n';
}
}

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.

How do iterators map/know their current position or element

Consider the following code example :
#include <vector>
#include <numeric>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <functional>
int main()
{
std::vector<int> v(10, 2);
std::partial_sum(v.cbegin(), v.cend(), v.begin());
std::cout << "Among the numbers: ";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
if (std::all_of(v.cbegin(), v.cend(), [](int i){ return i % 2 == 0; })) {
std::cout << "All numbers are even\n";
}
if (std::none_of(v.cbegin(), v.cend(), std::bind(std::modulus<int>(),
std::placeholders::_1, 2))) {
std::cout << "None of them are odd\n";
}
struct DivisibleBy
{
const int d;
DivisibleBy(int n) : d(n) {}
bool operator()(int n) const { return n % d == 0; }
};
if (std::any_of(v.cbegin(), v.cend(), DivisibleBy(7))) {
std::cout << "At least one number is divisible by 7\n";
}
}
If we look at this part of the code :
if (std::all_of(v.cbegin(), v.cend(), [](int i){ return i % 2 == 0; })) {
std::cout << "All numbers are even\n";
}
which is fairly easy to understand. It iterates over those vector elements , and finds out i%2==0 , whether they are completely divisible by 2 or not , hence finds out they're even or not.
Its for loop counterpart could be something like this :
for(int i = 0; i<v.size();++i){
if(v[i] % 2 == 0) areEven = true; //just for readablity
else areEven = false;
}
In this for loop example , it is quiet clear that the current element we're processing is i since we're actually accessing v[i]. But how come in iterator version of same code , it maps i or knows what its current element is that we're accessing?
How does [](int i){ return i % 2 == 0; }) ensures/knows that i is the current element which iterator is pointing to.
I'm not able to makeout that without use of any v.currently_i_am_at_this_posiition() , how is iterating done. I know what iterators are but I'm having a hard time grasping them. Thanks :)
Iterators are modeled after pointers, and that's it really. How they work internally is of no interest, but a possible implementation is to actually have a pointer inside which points to the current element.
Iterating is done by using an iterator object
An iterator is any object that, pointing to some element in a range of
elements (such as an array or a container), has the ability to iterate
through the elements of that range using a set of operators (with at
least the increment (++) and dereference (*) operators).
The most obvious form of iterator is a pointer: A pointer can point to
elements in an array, and can iterate through them using the increment
operator (++).
and advancing it through the set of elements. The std::all_of function in your code is roughly equivalent to the following code
template< class InputIt, class UnaryPredicate >
bool c_all_of(InputIt first, InputIt last, UnaryPredicate p)
{
for (; first != last; ++first) {
if (!p(*first)) {
return false; // Found an odd element!
}
}
return true; // All elements are even
}
An iterator, when incremented, keeps track of the currently pointed element, and when dereferenced it returns the value of the currently pointed element.
For teaching's and clarity's sake, you might also think of the operation as follows (don't try this at home)
bool c_all_of(int* firstElement, size_t numberOfElements, std::function<bool(int)> evenTest)
{
for (size_t i = 0; i < numberOfElements; ++i)
if (!evenTest(*(firstElement + i)))
return false;
return true;
}
Notice that iterators are a powerful abstraction since they allow consistent elements access in different containers (e.g. std::map).

c++11 insert a vector into a particular position

I am using c++11, I like to insert a vector into a particular position of another vector, here is a simplified code:
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v1 = {1, 2, 3};
vector<int> v2 = {0, 3, 9};
vector<int>::iterator itr = find(v2.begin(), v2.end(), 3);
itr = v2.erase(itr);
// like to insert "3", "2" and "1" to the same position which ends up "1", "2" and "3"
for (auto ri = v1.rbegin(); ri != v1.rend(); ++ri) {
v2.insert(itr, *ri);
}
for (const auto &i : v2) {
cout << i << " ";
}
cout << endl;
return 0;
}
The above code crashes.
Yes, I know some other STL API like transform() or copy() might be the one to use, but just wonder what is wrong of the above code?
Your current code crashes because itr is invalidated when insert() reallocates v2 after it exceeds its max capacity.
Change the following:
v2.insert(itr, *ri);
to
itr = v2.insert(itr, *ri);
Use this API
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
So in your case, replace the second loop with:
insert(itr,v2.begin(),v2.end())
what is wrong of the above code?
The iterator itr became invalid after insert, so program crashed at the 2nd execution of the for loop. You can use the return value of insert (iterator pointing to the inserted value) to get a valid iterator for insert position.
Change
v2.insert(itr, *ri);
to
itr = v2.insert(itr, *ri);
insert causes reallocation if the new size() is greater than the old capacity(). If the new size() is greater than capacity(), all iterators and references are invalidated. Otherwise, only the iterators and references before the insertion point remain valid. The past-the-end iterator is also invalidated.
Reference: http://en.cppreference.com/w/cpp/container/vector/insert
PS: Please see #Kam's answer, it's a better solution than hand-written loops in general cases.

Why can I not convert a reverse iterator to a forward iterator?

Well, I know why, it's because there isn't a conversion, but why isn't there a conversion? Why can forward iterators be turned to reverse iterators but not the other way round? And more importantly, what can I do if I want to do this? Is there some adapter that allows you to iterate backwards using a forward iterator?
std::vector<int> buffer(10);
std::vector<int>::iterator forward = buffer.begin();
std::vector<int>::reverse_iterator backward = buffer.rbegin();
++forward;
++backward;
std::vector<int>::iterator forwardFromBackward = std::vector<int>::iterator(backward); // error! Can't convert from reverse_iterator to iterator!
std::vector<int>::reverse_iterator backwardFromForward = std::vector<int>::reverse_iterator(forward); // this is fine
You could write a helper function. One particularity of reverse_iterator is that base() gives a forward iterator that is next from the value that the reverse iterator dereferences to. This is because a reverse iterator physically points to the element after the one it logically points to. So to have the forward iterator to the same item as your reverse_iterator, you'll need to decrement the result of base() by one, or you could increment the reverse iterator first, then take the .base() of that.
Both examples are shown below:
#include <iostream>
#include <vector>
#include <iterator>
//result is undefined if passed container.rend()
template <class ReverseIterator>
typename ReverseIterator::iterator_type make_forward(ReverseIterator rit)
{
return --(rit.base()); // move result of .base() back by one.
// alternatively
// return (++rit).base() ;
// or
// return (rit+1).base().
}
int main()
{
std::vector<int> vec(1, 1);
std::vector<int>::reverse_iterator rit = vec.rbegin();
std::vector<int>::iterator fit = make_forward(rit);
std::cout << *fit << ' ' << *rit << '\n';
}
Warning: this behavior is different from that of the reverse_iterator(iterator) constructor.
It's very common to have two (reverse) iterators span a range of values (such as in begin(),end() and rbegin(),rend()). For any range described by the two reverse iterators rA,rB, the range rB.base(),rA.base() will span the same range in the forward direction.
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::vector<int> vec{10,11,12,13,14,15};
// spans the range from 13 to 10
auto rfirst=std::rbegin(vec)+2;
auto rlast=std::rend(vec);
// Loops forward, prints 10 11 12 13
for(auto it = rlast.base(); it != rfirst.base(); ++it){
std::cout << *it << " ";
}
}
If conceptually you are only interested in a single item (such as the result of find_if), then use make_forward by #visitor. Even in this case, the range idea helps to keep track of the validity of the reverse iterator:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec{10,11,12,13,14,15};
auto rfirst=std::rbegin(vec);
auto rlast=std::rend(vec);
auto rfound = std::find_if(rfirst,rlast, [](int v){ return v<13; });
if(rfound != rlast){
std::cout << *rfound << " "; // prints 12
auto forwardFound = make_forward(rfound) ;
std::cout << *forwardFound << " "; // prints 12
}
}
You can get forward iterator from reverse iterator using this code
container.begin() + (reverseIter - container.rbegin() - 1);