ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Recently I have been coding in C++ STL. I have a deque where most of the time I want to insert at the end but occasionally I want to insert elements "almost" at the end. So I was looking to use the insert() function and use a reverse iterator. The code will compile with a normal iterator but not with a reverse iterator, is this normal? I thought that the reverse_iterator was just a specialisation of iterator.
I can get the code to work but this approach is not as efficient as I would like, since I know that the data will be added at or towards the end of the list.
This code compiles, but approached the structure from the "wrong" direction...
Code:
std::deque<StoredLine*>::iterator iter;
for (iter = markedLines.begin()
;iter != markedLines.end()
;iter++)
{
if ((*iter)->lineNo() == sl->lineNo())
{
(*iter)->rep();
break;
}
if ((*iter)->lineNo() > sl->lineNo())
{
if (action == '+')
sl->add();
else
sl->sub();
markedLines.insert(iter,sl);
break;
}
}
This upsets the compiler...
Code:
std::deque<StoredLine*>::reverse_iterator iter;
for (iter = markedLines.rbegin()
;iter != markedLines.rend()
;iter++)
{
if ((*iter)->lineNo() == sl->lineNo())
{
(*iter)->rep();
break;
}
if ((*iter)->lineNo() > sl->lineNo())
{
if (action == '+')
sl->add();
else
sl->sub();
markedLines.insert(iter,sl); // <<< compile error
break;
}
}
The compiler states:
access.cpp:62: error: no matching function for call to ‘std::deque<StoredLine*, std::allocator<StoredLine*> >::insert(std::reverse_iterator<std::_Deque_iterator<StoredLine*, StoredLine*&, StoredLine**> >&, StoredLine*&)’
access.cpp:66: error: no match for ‘operator==’ in ‘iter == std::deque<_Tp, _Alloc>::end() [with _Tp = StoredLine*, _Alloc = std::allocator<StoredLine*>]()’
i havent done much (what i would call 'actual') programming such as this so i cant give you an example or comments from experiences, but i give you this quote (which you may or may not have seen) as i think its relevant:
Quote:
Each of the container classes is associated with a type of iterator, and each of the STL algorithms uses a certain type of iterator.
std::deque<StoredLine*>::iterator iter;
for (iter = --markedLines.end()
;iter != markedLines.begin()-1
//this maybe better as being checked to end() as end is just an invalid iterator(i seem to recall)
;--iter)
{
if ((*iter)->lineNo() == sl->lineNo())
{
(*iter)->rep();
break;
}
//should this be ((*iter)->lineNo() < sl->lineNo())
if ((*iter)->lineNo() > sl->lineNo())
{
if (action == '+')
sl->add();
else
sl->sub();
markedLines.insert(iter,sl);
break;
}
}
or create a iterator from the reverse iterator
Code:
std::deque<StoredLine*>::reverse_iterator iter;
for (iter = markedLines.rbegin()
;iter != markedLines.rend()
;iter++)
{
if ((*iter)->lineNo() == sl->lineNo())
{
(*iter)->rep();
break;
}
if ((*iter)->lineNo() > sl->lineNo())
{
if (action == '+')
sl->add();
else
sl->sub();
//&*iter will create a bi direction iterator
markedLines.insert(&*iter,sl);
break;
}
}
nadroj, thanks for the link. I'm now back to trusty dial-up and so I don't spend as much time searching the net as I would like. I have been to the site you mentioned before but not while investigating this problem. The quote kind of reinforced what I was thinking, it's just that the few texts I have with me are a little skimpy in this area of C++ ( I guess I need to try and get a STL tome but I normally use QT, it's just that for this project I didn't want to use any external libraries)
dmail, I tried many ways of trying to convert a reverse_iterator to an iterator without any joy and I had also tried what you suggested in the first listing but only got seg-faults. However, your suggestion spurred me on to again. The code you gave was slightly different to my attempt but it also seg-faults, but I was able to modify it to get it working. Apparently it doesn't like --markedLines.end(), or even the more explicit, --(markedLines.end()).
However, I was able to extend your idea to get the following:
Code:
std::deque<StoredLine*>::iterator iter;
for (iter = markedLines.end()
;iter != markedLines.begin()
;)
{
iter--;
if ((*iter)->lineNo() == sl->lineNo())
{
(*iter)->rep();
break;
}
if ((*iter)->lineNo() > sl->lineNo())
{
if (action == '+')
sl->add();
else
sl->sub();
markedLines.insert(iter,sl);
break;
}
}
It works and so I'm now a happy bunny. Thanks everyone.
Having just read a little more in C++ in a Nutshell I have found the following quote:
Quote:
Reverse iterators share a problem with const_iterators, namely that several members, such as insert and erase, do not take an iterator template parameter but require the exact iterator type, as declared in the container class.
So there it is in black and white I need to use the iterator for insertion. Once again thanks to everyone.
I don't have time to check at the moment but im pretty sure --end() is valid and if its not then end()- 1 is. Im also pretty sure &*iter is valid.
The following seems to confirm this:
[3] Note the implications of this remark. The variable rfirst is initialized as reverse_iterator<...> rfirst(V.end());. The value obtained when it is dereferenced, however, is *(V.end() - 1). This is a general property: the fundamental identity of reverse iterators is &*(reverse_iterator(i)) == &*(i - 1). This code sample shows why this identity is important: if [f, l) is a valid range, then it allows [reverse_iterator(l), reverse_iterator(f)) to be a valid range as well. Note that the iterator l is not part of the range, but it is required to be dereferenceable or past-the-end. There is no requirement that any such iterator precedes f.
There's some red flags in the code
1. Always use preincrement or predecrement unless there is a reason not to
2. Both break statements do nothing
3. The insert statement is not part of the else clause
4. Iterating and mutating a collection simultaneously is potentially problematic
2. Both break statements do nothing
3. The insert statement is not part of the else clause
4. Iterating and mutating a collection simultaneously is potentially problematic
tuxdev from me viewing this code (and from knowing graemef from this board)I suspect only one line is getting inserted here. The line is checked to see if its a replication if so nothing is done to the queue, or if its found its home it is either an addition or subtraction and then inserted. The break statements do work, they break out of the loop thats why I say one line is getting inserted.
I'll retract the "both breaks do nothing", cause I forgot what a for loop was for a moment there. Since it's breaking as soon as it does insert, it's relatively safe too.
But that the insert is not part of the else even though the indentation implies it is is still a problem.
The insert is not meant to be part of the else, as I was playing around with various possibilities the indentation got messed up, which is actually a good argument for those braces! I actually want to remove the if - else by rewriting the add() and sub() methods.
I realise that changing the collection and still using the iterator is a recipe for future heartache. Here I use the iterator only to find out where to insert it in the deque, but I should probably add an comment just for future safeguard.
access.cpp:63: error: no matching function for call to ‘std::deque<StoredLine*, std::allocator<StoredLine*> >::insert(StoredLine**, StoredLine*&)’
And all versions that I tried of iter = --(markedLines.end()) or iter = markedLines.end()-1 caused a segfault as soon at the iter was used. By the way markedLines is passed into this method as a reference, not certain if that makes a difference though.
I'm not convinced by the optimisation argument, if it is a trivial increment then it will result in trivial assembly which will not result in any additional statements. If it is non trivial then the order probably is important.
Anyway, I had missed the base() method. Just what I needed. So I now have the following.
Code:
std::deque<StoredLine*>::reverse_iterator iter;
for (iter = markedLines.rbegin()
;iter != markedLines.rend()
;iter++)
{
if ((*iter)->lineNo() == sl->lineNo())
{
(*iter)->action(newAction);
break;
}
if ((*iter)->lineNo() < sl->lineNo())
{
sl->action(newAction);
// use base() to "cast" the reverse_iterator into an iterator
markedLines.insert(iter.base(),sl);
break;
}
} // end of stepping through the deque on the reverse iterator
So thanks for showing me the base() method, I feel that it makes the code a little cleaner.
For non-built-in types, it is never trivial and the compiler can't optimize the postincrement into a preincrement for you, even though you really don't care. Since programs have a tendency to use the increment operators a lot, using preincrement instead of postincrement along with other idiomatic optimizations can make a difference.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.