I have a std::list<double> foo;
I'm using
if (foo.size() >= 2){
double penultimate = *(--foo.rbegin());
}
but this always gives me an arbitrary value of penultimate.
What am I doing wrong?
Rather than decrementing rbegin, you should increment it, as shown here:1
double penultimate = *++foo.rbegin();
as rbegin() returns a reverse iterator, so ++ is the operator to move backwards in the container. Note that I've also dropped the superfluous parentheses: that's not to everyone's taste.
Currently the behaviour of your program is undefined since you are actually moving to end(), and you are not allowed to dereference that. The arbitrary nature of the output is a manifestation of that undefined behaviour.
1Do retain the minimum size check that you currently have.
The clearest way, in my mind, is to use the construct designed for this purpose (C++11):
double penultimate = *std::prev(foo.end(), 2)
I would just do *--(--foo.end()); no need for reverse iterators. It's less confusing too.
Related
I am implementing a container that presents a map-like interface. The physicals implementation is an std::vector<std::pair<K*, T>>. A K object remembers its assigned position in the vector. It is possible for a K object to get destroyed. In that case its remembered index is used to zero out its corresponding key pointer within the vector, creating a tombstone.
I would like to expose the full traditional collection of iterators, though I think that they need only claim to be forward_iterators (see next).
I want to be able to use range-based for loop iteration to return the only non-tombstoned elements. Further, I would like the implementation of my iterators to be a single pointer (i.e. no back pointer to the container).
Since the range-based for loop is pretested I think that I can implement tombstone skipping within the inequality predicate.
bool operator != (MyInterator& cursor, MyIterator stop) {
while (cursor != stop) {
if (cursor->first)
return true;
++cursor;
}
return false;
}
Is this a reasonable approach? If yes, is there a simple way for me to override the inequality operator of std::vector's iterators instead of implementing my iterators from scratch?
If this is not a reasonable approach, what would be better?
Is this a reasonable approach?
No. (Keep in mind that operator!= can be used outside a range-based for loop.)
Your operator does not accept a const object as its first parameter (meaning a const vector::iterator).
You have undefined behavior if the first parameter comes after the second (e.g. if someone tests end != cur instead of cur != end).
You get this weird case where, given iterators a and b, it might be that *a is different than *b, but if you check if (a != b) then you find that the iterators are equal and then *a is the same as *b. This probably wrecks havoc with the multipass guarantee of forward iterators (but the situation is bizarre enough that I would want to check the standard's precise wording before passing judgement). Messing with people's expectations is inadvisable.
There is no simple way to override the inequality operator of std::vector's iterators.
If this is not a reasonable approach, what would be better?
You already know what would be better. You're just shying away from it.
Implement your own iterators from scratch. Wrapping your vector in your own class has the benefit that only the code for that class has to be aware that tombstones exist.
Caveat: Document that the conditions that create a tombstone also invalidate iterators to that element. (Invalid iterators are excluded from most iterator requirements, such as the multipass guarantee.)
OR
While your implementation makes a poor operator!=, it could be a fine update or check function. There's this little-known secret that C++ has more looping structures than just range-based for loops. You could make use of one of these, for example:
for ( cur = vec.begin(); skip_tombstones(cur, vec.end()); ++cur ) {
auto& element = *cur;
where skip_tombstones() is basically your operator!= renamed. If not much code needs to iterate over the vector, this might be a reasonable option, even in the long term.
A very simpel example is multiplication - suppose I have a vector:
std::vector<int> ints = {1,2,3,4};
With a naive approach I can just use std::accumulate (or std::reduce) and it looks like this:
int result = std::accumulate(ints.begin(), ints.end(), int{}, [](const int &a, const int &b){return a*b;});
but since the initial value is zero - the result becomes zero as well (For this specific case, one way I could fix it is by putting a '1' as initial).
I would rather use an algorithm that does the above but without an initial value 'side-effect' (ie. just multiply the numbers in vector).
A similar problem is often encountered within string handling where a delimiter must be inserted between elements.
What you're talking about can be reframed as a generalisation of accumulate over the last N-1 elements of your range, with the 1st element being the initial value.
So you can just write:
std::accumulate(std::next(std::begin(ints)), std::end(ints), *std::begin(ints), OP);
You have to assume that ints is non-empty, though, which raises my main point: what should a hypothetical standard function return when the range is empty? Should its results simply be undefined? Is that sensible?
(current draft) 237) accumulate is similar to the APL reduction operator and Common Lisp reduce function, but it avoids the difficulty of defining the result of reduction on an empty sequence by always requiring an initial value
Accumulate sidesteps this issue and provides a boatload of flexibility, by doing things the way it does. I think that's a good thing.
Combined with the ability to simply provide an appropriate initial value like 1 for your operation over the whole range, I'm not convinced there's much need for this hypothetical alternative in the standard.
It might also be difficult to come up with two names for it that mirror the already-asymmetrically-named "accumulate" and "reduce".
template <class InputIt, class T, class BinaryOperation>
T fold_if_you_really_want_to(InputIt first, InputIt last, BinaryOperation op)
{
// UB if range is empty. Whatevs.
T init = *first;
return std::accumulate(++first, last, std::move(init), std::move(op));
}
…or something like that anyway. Note that this necessarily copies the first element; you could avoid that if you weren't lazy by calling into std::accumulate like I did. 😊
In addition to #Lightness Races in Orbit's answer, consider the case in Haskell:
For cases like you described it (most prominently searching the maximum element in a list), Haskell delivers the functions foldl1 and foldr1, which perform the fold over the collection and implicitly taking the first value as initial value.
Yes, for the empty list this makes no sense, hence for this problem you have to provide a list with at least one element.
Is it valid to do this on an input iterator *it++ ?
I understand the code as follow, that it dereference the iterator and gives me the value and then step one ahead.
In the c++ reference the * operator is lower than the postfix operator: http://en.cppreference.com/w/cpp/language/operator_precedence
But I read this style is bad practice. Why?
Is it valid to do this on an input iterator *it++?
Yes, that is valid. The iterator will be incremented, its previous value will be returned, and you will dereference that returned iterator.
But I read this style is bad practice. Why?
Consider these two implementations I've just pulled out of some graph code I wrote a while back:
// Pre-increment
BidirectionalIterator& operator++()
{
edge = (edge->*elem).next;
return *this;
}
// Post-increment
BidirectionalIterator operator++(int)
{
TargetIterator oldval(list, edge);
edge = (edge->*elem).next;
return oldval;
}
Notice that for post-increment, I need to first construct a new iterator to store the previous value which will be returned.
If it's simple and clear to write your program to make use of pre-increment, there may be better performance, less work for the compiler to do, or both.
Just don't go crazy on this (for example, rewriting all your loops)! That would likely be micro-optimization. However, the reason people say it's good practice is that if you get into a habit of using pre-increment as default then you get (potentially) better performance by default.
The STL has many functions that return iterators. For example, the STL list function erase returns an iterator (both C++98 and C++11). Nevertheless, I see it being used as a void function. Even the cplusplus.com site has an example that contains the following code:
mylist.erase(it1, it2);
which does not return an iterator. Shouldn't the proper syntax be as follows?
std::list<int>::iterator iterList = mylist.erase(it1, it2)?
You are not forced to use a return value in C++. Normally the returned iterator should be useful to have a new valid iterator to that container. But syntactically it's correct. Just keep in mind that after the erasure, it1 and it2 will not be valid any more.
which does not return an iterator. Shouldn't the proper syntax be as
follows?
It does return an iterator, but just as with any other function you can ignore the returned value. If you just want to erase an element and dont care about the returned iterator, then you may simply ignore it.
Actually ignoring the return value is more common than you might expect. Maybe the most common place where return values are ignored is when the only purpose of the return value is to enable chaining. For example the assignment operator is usually written as
Foo& operator=(const Foo& other){
/*...*/
return *this;
}
so that you can write something like a = b = c. However, when you only write b = c you usually ignore the value returned by that call. Also note, that when you write b = c this does return a value, but if you just want to make this assignment, then you have no other choice than to ignore the return value.
The actual reason is far more banal than you might think. If you couldn't ignore a return value, many of the functions like .erase() would need to be split in two versions: an .erase() version returning void and another .erase_and_return() version which returned the iterator. What would you gain from this?
Return values are sometimes that are sometimes useful. If they are not useful, you don't have to use them.
All container.erase functions return the iterator after the newly erased range. For non-node-based containers this is often (but not always) useful because the iterators at and after the range are no longer valid.
For node-based containers this is usually useless, as the 2nd iterator passed in remains valid even after the erase operation.
Regardless, both return that iterator. This permits code that works on a generic container to not have to know if the container maintains valid iterators after the erase or not; it can just store the return value and have a valid iterator to after-the-erase.
Iterators in C++ standard containers are all extremely cheap to create and destroy and copy; in fact, they are in practice so cheap that compilers can eliminate them entirely if they aren't used. So returning an iterator that isn't used can have zero run time cost.
A program that doesn't use this return value here can be a correct program, both semantically and syntactically. At the same time, other semantically and syntactically correct programs will require that you use that return value.
Finally,
mylist.erase(it1, it2);
this does return an iterator. The iterator is immediately discarded (the return value only exists as an unnamed temporary), and compilers are likely to optimize it out of existence.
But just because you don't store a return value, doesn't mean it isn't returned.
I was reading today about how for containers that support bidirectional iteration, this piece of code is valid:
Collection c(10, 10);
auto last = --c.end();
*last;
That got me thinking, is it required that when submitting a pair of bidirectional iterators [beg, end) to an algorithm in the STL that --end is defined? If so, should the result be dereferenceable?
ie
void algo(T beg, T end){
//...
auto iter = --end;
//...
*iter;
}
If the algorithm requires a range defined by bidirectional iterators first and last, then --last needs to be valid under the same conditions that ++first does -- namely that the range isn't empty. The range is empty if and only if first == last.
If the range isn't empty, then --last evaluates to an iterator that refers to the last element in the range, so *--last indeed also needs to be valid.
That said, there aren't all that many standard algorithms that require specifically a bidirectional iterator (and don't require random-access). prev, copy_backward, move_backward, reverse, reverse_copy, stable_partition, inplace_merge, [prev|next]_permutation.
If you look at what some of those do, you should see that typically the algorithm does decrement the end-of-range iterator and dereference the result.
As James says, for containers the function end() returns an iterator by value. There is no general requirement that for iterators that --x should be a well-formed expression when x is an rvalue of the type. For example, pointers are bidirectional iterators, and a function declared as int *foo(); returns a pointer by value, and --foo() is not a well-formed expression. It just so happens that for the containers you've looked at in your implementation, end() returns a class type which has operator-- defined as a member function, and so the code compiles. It also works since the container isn't empty.
Be aware that there is a difference in this respect between:
auto last = --c.end();
vs.
auto last = c.end();
--last;
The former decrements an rvalue, whereas the latter decrements an lvalue.
You read wrong. The expression --c.end() is never authorized. If the
iterator isn't at least bidirectional, it is, in fact, expressedly
forbidden, and requires a compiler error. If the collection is empty,
it is undefined behavior. And in all other cases, it will work if
it compiles, but there is no guarantee that it will compile. It failed
to compile with many early implementations of std::vector, for
example, where the iterator was just a typedef to a pointer. (In fact,
I think formally that it is undefined behavior in all cases, since
you're violating a constraint on a templated implementation. In
practice, however, you'll get what I just described.)
Arguably, because it isn't guaranteed, a good implementation will cause
it to fail to compile, systematically. For various reasons, most don't.
Don't ask me why, because it's incredibly simple to get it to fail
systematically: just make the operator-- on the iterator a free
function, rather than a member.
EDIT (additional information):
The fact that it isn't required is probably a large part of the
motivation behind std::next and std::prev in C++11. Of course,
every project I've worked on has had them anyway. The correct way to
write this is:
prev( c.end() );
And of course, the constraints that the iterator be bidirectional or
better, and that the container not be empty, still hold.
Each algorithm will tell you what type of iterator it requires. When a bidirectional iterator is called for, then naturally it will need to support decrementing.
Whether --end is possible depends on whether end == beg.
It's only required for algorithms that require bidirectional iterators.