I want to insert something into a STL list in C++, but I only have a reverse iterator. What is the usual way to accomplish this?
This works: (of course it does)
std::list<int> l;
std::list<int>::iterator forward = l.begin();
l.insert(forward, 5);
This doesn't work: (what should I do instead?)
std::list<int> l;
std::list<int>::reverse_iterator reverse = l.rbegin();
l.insert(reverse, 10);
l.insert(reverse.base(), 10); will insert '10' at the end, given your definition of the 'reverse' iterator. Actually, l.rbegin().base() == l.end().
Essentially, you don't. See 19.2.5 in TCPPPL.
The reverse_iterator has a member called base() which will return a "regular" iterator. So the following code would work in your example:
l.insert(reverse.base(), 10);
Be careful though because the base() method returns the element one after the orginal reverse_iterator had pointed to. (This is so that reverse_iterators pointing at rbegin() and rend() work correctly.)
Just in case it is helpful, as this it the first hit on a search, here is a working example of using rbegin. This should be faster than using std::stringstream or sprint. I defiantly call a member fmt_currency many thousands of times in some print jobs. The std::isalnum is for handling a minus sign.
std::wstring fmt_long(long val) {//for now, no options? Just insert commas
std::wstring str(std::to_wstring(val));
std::size_t pos{ 0 };
for (auto r = rbegin(str) + 1; r != str.rend() && std::isalnum(*r); ++r) {
if (!(++pos % 3)) {
r = std::make_reverse_iterator(str.insert(r.base(), L','));
}
}
return str;
}
Related
Since we now have advance() and the prev() to move iterator to go front or go back, and we already have begin() and end().
I wonder is there any situation we better/have to move reverse iterator back and front?
Algorithms often take two iterators that specify a range of elements. For example std::for_each:
std::vector<int> x;
std::for_each(x.begin(),x.end(),foo);
If you want to make for_each iterate in reverse order (note: for_each does iterate in order) then neither advance nor prev are of any help, but you can use reverse iterators:
std::for_each(x.rbegin(),x.rend(),foo);
Because using begin() and end() to iterate in reverse looks horrible:
std::vector<int> v {1, 2, 3};
if(!v.empty()) { //need to make sure of that before we decrement
for(auto it = std::prev(v.end()); ; --it) {
//do something with it
if(it == v.begin()) {
break;
}
}
}
Compare it with reverse iterator version:
std::vector<int> v {1, 2, 3};
for(auto it = v.rbegin(); it != v.rend(); it++) {
//do something with it
}
When you have a function template that takes iterators, and want it to operate on the data in reverse.
E.g.
std::string s = "Hello";
std::string r(s.rbegin(), s.rend());
std::cout << r;
When you use algorithms like std::for_each(), std::accumulate(), std::find_if()... they systematically progress with ++.
If you want this progression to physically occur backwards, then the reverse
iterators are useful.
I guess it is good practise because it seems odd if you start from end and finish in begin. You can easily say last but one by using rbegin.
vector::reverse_iterator itr1;
for (itr1 = vec.rbegin(); itr1 < vec.rend(); itr1++) {
if (*itr1 == num) {
vec.erase((itr1 + 1).base());
}
}
You can use as a function which deletes that Which num want to erase in vector
The need for rbegin()/rend() is because begin() is not the same as rend(), and end() is not rbegin(), see this image from cppreference
This way, you can use any algorithm going forward from beginning to end or backwards from the last to the first element.
There are examples with for each. However, more general, it allows you to reuse any algorithm or operators that works with iterators with advancing, to do the same thing but in a reverse order.
I have a vector of objects and want to delete by value. However the value only occurs once if at all, and I don't care about sorting.
Obviously, if such delete-by-values were extremely common, and/or the data set quite big, a vector wouldn't be the best data structure. But let's say I've determined that not to be the case.
To be clear, if my code were C, I'd be happy with the following:
void delete_by_value( int* const piArray, int& n, int iValue ) {
for ( int i = 0; i < n; i++ ) {
if ( piArray[ i ] == iValue ) {
piArray[ i ] = piArray[ --n ];
return;
}
}
}
It seems that the "modern idiom" approach using std::algos and container methods would be:
v.erase(std::remove(v.begin(), v.end(), iValue), v.end());
But that should be far slower since for a random existent element, it's n/2 moves and n compares. My version is 1 move and n/2 compares.
Surely there's a better way to do this in "the modern idiom" than erase-remove-idiom? And if not why not?
Use std::find to replace the loop. Take the replacement value from the predecessor of the end iterator, and also use that iterator to erase that element. As this iterator is to the last element, erase is cheap. Bonus: bool return for success checking and templateing over int.
template<typename T>
bool delete_by_value(std::vector<T> &v, T const &del) {
auto final = v.end();
auto found = std::find(v.begin(), final, del);
if(found == final) return false;
*found = *--final;
v.erase(final);
return true;
}
Surely there's a better way to do this in "the modern idiom" than erase-remove-idiom?
There aren't a ready-made function for every niche use case in the standard library. Unstable remove is one of the functions that is not provided. It has been proposed (p0041r0) a while back though. Likewise, there are also no special versions of algorithms for the special case of vectors that do not contain duplicates.
So, you'll need to implement the algorithm yourself if you wish to use an optimal algorithm. There is std::find for linear search. After that, you only need to assign from last element and finally pop it off.
Most implementations of std::vector::resize will not reallocate if you make the size of the vector smaller. So, the following will probably have similar performance to the C example.
void find_and_delete(std::vector<int>& v, int value) {
auto it = std::find(v.begin(), v.end(), value);
if (it != v.end()) {
*it = v.back();
v.resize(v.size() - 1);
}
}
C++ way would be mostly identical with std::vector:
template <typename T>
void delete_by_value(std::vector<T>& v, const T& value) {
auto it = std::find(v.begin(), v.end(), value);
if (it != v.end()) {
*it = std::move(v.back());
v.pop_back();
}
}
I have the following very basic question. I want to use stl iterators instead of traditional C-type pointers for filling an array in a function. By the C-style way I mean the following example:
void f(double* v, size_t n) {
for (int i = 0; i < n; i++)
v[i] = 10; /* a more reasonable value in practice! */
}
I would convert this to the C++ style using the iterators as follows:
void f(vector<double>::const_iterator first, vector<double>::const_iterator last) {
for(vector<double>::iterator it = first; it != last; it++)
*it = 10;
}
But I get compilation errors. If I use iterator instead of const_iterator the problem will be solved. However, I was wondering if that is the correct way? Because I thought vector.begin() and vector.end() iterators are constant.
Thanks in advance!
The difference between
const vector<double>::iterator
and
vector<double>::const_iterator
is roughly the same as between double * const v and const double *v:
the first says that the iterator must remain constant, but what it points to can be changed
the second says that the iterator itself is changeable, but what it points to is const.
If you rewrite the function as
void f(const vector<double>::iterator first, const vector<double>::iterator last) {
for(vector<double>::iterator it = first; it != last; it++)
*it = 10;
}
it would compile and run correctly.
What you see is due to the fact that const_iterator's correspond roughly to pointers to const. So you can change the value of the iterator, i.e. make it point somewhere else, but you cannot modify what it points to.
This is different from const iterators, which would not allow incrementing or decrementing them. Here is an example:
#include <vector>
int main() {
std::vector<int> v{ 1, 2, 3 };
std::vector<int>::const_iterator i = v.begin();
*i = 10; // ERROR!
++i; // OK
std::vector<int>::iterator const ci = v.begin();
*ci = 10; // OK
++ci; // ERROR!
}
std::fill(my_vector.begin(), my_vector.end(), 10);
The problem is that your functions takes const_iterators but your loop needs an iterator, since you want to modify the data. The solution is of course to let your function take iterators right away, since it is obviously meant to modify the range.
This doesn't have anything to do with what vector.begin() returns. For a const object or reference they will return const_iterators, otherwise they'll return iterators. But your function definitely needs iterators, since it modifies the values in the range passed to it.
Since you're using const_iterators, you can't modify the vector. Using non-const iterators is the right thing to do.
In answer to your last question, vector.begin() and vector.end() have both const_ and non-const_ implementations. If your vector is non-const, you'll get a non-const_ iterator. See the documentation for std::vector::begin.
std::vector::erase() does not accept reverse iterator.
Is there any way to call this method with a reverse iterator?
My sample code is:
std::vector<int> MyVector;
for (int i=0; i<10; i++)
{
MyVector.push_back(i);
}
// Now suppose that I want to erase the last three elements
int nEraseCount = 0;
for (std::vector<int>::const_reverse_iterator it=MyVector.rbegin();
it<MyVector.rend(); ++it)
{
MyVector.erase(it);
if (++nEraseCount == 3) break;
}
However, this sample code is not working, because it is a reverse iterator and erase() does not take reverse iterator as its argument.
How do I modify this code so that it works?
You can convert from reverse_iterators to iterators using base() although you need to subtract one to get the one that points to the same element thus rbegin() points to end() and rend() points to begin() (because it is not possible to point to one before the beginning in reality).
You have more of a problem because you are using a const_reverse_iterator which cannot be converted to a non-const one and erase requires non-const iterators. The logic is that you are modifying the collection so you use a non-const iterator.
In your case, you have a bigger problem with your loop as you are removing iterators thus invalidating them, then trying to move back to the previous element.
If you need to remove the last 3 elements then you should use an erase method that takes a range rather than remove them one at a time.
This can be done using MyVector.erase(MyVector.rbegin() + 3).base(), MyVector.end() ) in this particular case as long as you know that MyVector.size() >= 3
I would workaround the problem by not using a reverse iterator. I'll probably write something like that:
std::vector<int> MyVector;
for (int i=0; i<10; i++)
{
MyVector.push_back(i);
}
// Now suppose that I want to erase the last three elements
int nEraseCount = 0;
while (nEraseCount < 3 && !MyVector.empty())
{
MyVector.pop_back();
++nEraseCount;
}
Okay you have several options - you are erasing from the end - so you could:
resize()
if (MyVector.size() > 3)
MyVector.resize(MyVector.size() - 3);
else
MyVector.clear(); // presumably you don't want all anyway!
simple difference
if (MyVector.size() > 3)
MyVector.erase(MyVector.end() - 3, MyVector.end());
else
MyVector.clear(); // presumably you don't want all anyway!
The approach you've taken is not very idiomatic
If you just want to remove N elements at the back:
size_t N = 3;
size_t to_remove = std::min(vec.size(), N);
vec.erase(vec.end() - to_remove, vec.end());
You cannot pass const_iterator or const_reverse_iterator to erase(), as it is readonly iterator!
You should use non-const forward iterator version : std::vector<int>::iterator.
I'm working with iterators on C++ and I'm having some trouble here. It says "Debug Assertion Failed" on expression (this->_Has_container()) on line interIterator++.
Distance list is a vector< vector< DistanceNode > >. What I'm I doing wrong?
vector< vector<DistanceNode> >::iterator externIterator = distanceList.begin();
while (externIterator != distanceList.end()) {
vector<DistanceNode>::iterator interIterator = externIterator->begin();
while (interIterator != externIterator->end()){
if (interIterator->getReference() == tmp){
//remove element pointed by interIterator
externIterator->erase(interIterator);
} // if
interIterator++;
} // while
externIterator++;
} // while
vector's erase() returns a new iterator to the next element. All iterators to the erased element and to elements after it become invalidated. Your loop ignores this, however, and continues to use interIterator.
Your code should look something like this:
if (condition)
interIterator = externIterator->erase(interIterator);
else
++interIterator; // (generally better practice to use pre-increment)
You can't remove elements from a sequence container while iterating over it — at least not the way you are doing it — because calling erase invalidates the iterator. You should assign the return value from erase to the iterator and suppress the increment:
while (interIterator != externIterator->end()){
if (interIterator->getReference() == tmp){
interIterator = externIterator->erase(interIterator);
} else {
++interIterator;
}
}
Also, never use post-increment (i++) when pre-increment (++i) will do.
I'll take the liberty to rewrite the code:
class ByReference: public std::unary_function<bool, DistanceNode>
{
public:
explicit ByReference(const Reference& r): mReference(r) {}
bool operator()(const DistanceNode& node) const
{
return node.getReference() == r;
}
private:
Reference mReference;
};
typedef std::vector< std::vector< DistanceNode > >::iterator iterator_t;
for (iterator_t it = dl.begin(), end = dl.end(); it != end; ++it)
{
it->erase(
std::remove_if(it->begin(), it->end(), ByReference(tmp)),
it->end()
);
}
Why ?
The first loop (externIterator) iterates over a full range of elements without ever modifying the range itself, it's what a for is for, this way you won't forget to increment (admittedly a for_each would be better, but the syntax can be awkward)
The second loop is tricky: simply speaking you're actually cutting the branch you're sitting on when you call erase, which requires jumping around (using the value returned). In this case the operation you want to accomplish (purging the list according to a certain criteria) is exactly what the remove-erase idiom is tailored for.
Note that the code could be tidied up if we had true lambda support at our disposal. In C++0x we would write:
std::for_each(distanceList.begin(), distanceList.end(),
[const& tmp](std::vector<DistanceNode>& vec)
{
vec.erase(
std::remove_if(vec.begin(), vec.end(),
[const& tmp](const DistanceNode& dn) { return dn.getReference() == tmp; }
),
vec.end()
);
}
);
As you can see, we don't see any iterator incrementing / dereferencing taking place any longer, it's all wrapped in dedicated algorithms which ensure that everything is handled appropriately.
I'll grant you the syntax looks strange, but I guess it's because we are not used to it yet.