I have written a circular buffer of size N. I've also written a custom iterator.
I'm using them for logic like this:
auto iter = circular_buffer.begin();
while(iter != circular_buffer.end())
{
++iter;
}
What is the implementation for end()? Usually it should point to the last element + 1, but if the buffer contains N items, last element + 1 will be the first element and the above code won't even loop once. I cannot do:
do
{
++iter;
} while(iter != circular_buffer.end());
because if the buffer is empty, it will execute once.
Is there a way to solve this?
Note: the following assumes that the circular nature of the buffer is a property that is being exposed to the user for their use, rather than being an implementation detail of the system (as is the case for std::list implementations).
Giving a circle a proper "range" is something of a problem since... it's a circle. It conceptually has neither a beginning nor an end.
One way to do this is to make the "circle" into a helix. Every iterator stores both whatever normal information it needs as well as a cycle count. When an iterator is incremented to whatever the arbitrary "beginning" of the list is, the cycle count is incremented. If the iterator is decremented to before the beginning of the list, the cycle count is decremented. Two iterators are only equal if their position and cycle counts are equal.
Most iterators created by the circular list should have a cycle count of zero on construction. The end iterator should have a cycle count of 1 while having the same position in the circle as the begin iterator.
Of course, there are some implementation problems. Doing this requires that every iterator's increment/decrement operations to know where the "beginning" of the circle is. So they all need a pointer to the first element or something. But if they have that, then these iterators all become invalid if you modify the list, since that can change what the "first element" is. Now, invalidating iterators on container modification is something that is true of many container iterator types, so it's not too big of a problem. But it is something you should be aware of.
That's kind of an unavoidable downside of trying to use ranges with a concept that inherently has neither a beginning nor an end.
Related
I'm coming from a C background.
When iterating through a container such as a vector or deque which contains elements in contiguous memory, it's easy to understand that by incrementing the iterator to point to the next element in the vector, such as in the loop below, it may just increase the address of the iterator by the size of the element in the vector, in this case int.
vector<int> vec;
vector<int>::iterator it;
for (it = vec.begin; it != vec.end(); it++)
{
// do something on each element
}
The benefits i believe of using iterators is that we can decouple the algorithm code from the actual container type, so if we decide later to use a list instead of a vector, we can use the same iteration code as above on the list.
It is possible to iterate through a list in the exact same way, but a list will have elements not in contiguous memory locations, so how does the iterator increment and find the correct address of the next element?
My understanding from implementing linked lists in C is that we would check the pointer in the node, possibly called "next" which points to the next node in the list.
Is this exactly what's happening here in C++ behind the scenes when using list iterators? Therefore , when an iterator is pointing to a list element, in order to point to the next element, the compiler will generate completely different code to that of pointing to the next element in a vector? Thanks in advance.
You have the correct idea. The critical point is here:
when an iterator is pointing to a list element
A better phrasing would be “for a kind of iterator used with a linked list”: no one iterator can indicate an element of a vector or a list.
Because each container type has its own iterator type, and the type in use is always known to the compiler, the correct code can be generated (and optimized!) in all cases. (Generic code can be written with no explicit types, but the types must be specified (or in some cases inferred by the compiler) for each use.)
Note that not all iterator types are implemented using (only) a pointer: for example, a deque<T>::iterator typically bundles a pointer to a short array and an index into that array. The template system also supports this size and composition variation between data types with corresponding operations.
Suppose I have two iterators, which span entire stl-like container or its contiguous part, thus defining a range. This pair of iterators would be immutable. Then I would like to have a third, mutable iterator, which will point to any of the elements being part of the range.
This leads me to having an adaptor, for instance (metacode):
Adaptor<Iterator>:
==================
+ Iterator: begin
+ Iterator: end
+ Iterator: current
if current==begin then std::prev(adaptor) doesn't change current and still equals begin
if current==end then std::next(adaptor) doesn't change current and it still equals end
I tried to find out-of-the-shelf solutions in STL and Boost, but so far (might be my vocab/jargon limitation) I can't find any.
Should I implement such adaptor from scratch myself or I am missing some stl/boost goodies out there?
Thanks.
It would not be possible to implement this as an iterator. For std::prev/next to work on it, it must be a "bidirectional iterator" (in the sense of fulfilling all the requirements of the "bidirectional iterator" model). One of those requirements is "--r == --s implies r == s." But, of course, that wouldn't be true for two such iterators if one were immediately after begin and the other one were at begin.
I mean in situation when the iterators point on same element.
On http://www.cplusplus.com/reference/stl/list/erase/ say "Removes from the list container either a single element (position) or a range of elements ([first,last))." and
"first, last
Iterators specifying a range within the list container to be removed: [first,last). i.e., the range includes all the elements between first and last, including the element pointed by first but not the one pointed by last."
I totally don't know if I do everything wrong but for every part of my code I don't find needed information anywhere and when I want to test it by myself, I end in a situation, when I don't know what happened and after asking here and arguing for long hours I find something like "undefined behavior". So can someone help me faster, what is it now?
And I want to be better programmer and find out better source than cplusplus.com and cppreference.com, because they both suck, is there something better? I am getting crazier every day with this C++ (but I still think it's much better for speedy huge programs than Java or C), please help.
The Standard's own definition of ranges (24.2.1p7, emphasis mine):
Most of the library’s algorithmic templates that operate on data structures have interfaces that use ranges. A range is a pair of iterators that designate the beginning and end of the computation. A range [i,i) is an empty range; in general, a range [i,j) refers to the elements in the data structure starting with the element pointed to by i and up to but not including the element pointed to by j.
So assuming it is a valid iterator in or past-the-end of lst, the call lst.erase(it,it) erases an empty set of elements from lst. That is, it does nothing.
I think to best answer your question you should think about how iterators work and why everything is passed in as [first, last) and not something else.
There are two core rules about iterators that you need to keep in mind. You can always increment one (that is to say, first++) and two iterators that point to the same element will always be equal. Knowing this you can loop over ANY range of iterators with the logic:
for(; first != last; first++)
{
}
So, if first and last are equal, nothing will happen. So if you call list.erase(it, it) nothing will be erased.
To put it in a more general form. Any range in STL where first == last is effectively empty.
I have a dummy question. I always read that C++ std::list container has constant time for inserting elements at the beginning, at the end and in the middle:
Which is the correct way to insert an element directly in the middle of a std::list?
Is maybe this one?
std::list<int> l;
l.push_back(10);
l.push_back(20);
l.push_back(30);
l.push_back(40);
l.push_back(50);
l.push_back(60);
l.insert( l.end()- l.begin() /2 ); //? is this
// inserting directly in the middle?
When we say 'inserting in the middle' do we really mean that we save linear time to go from the beginning of the list to the desired point ( traversing one by one through all linked elements in between)?
You can do the iterator maths generically like this:
std::list<int>::iterator it = l.begin();
std::advance(it, std::distance(l.begin(), l.end())/2);
l.insert(it, value);
This will work for any iterator type (except OutputIterator or InputIterator)
Of course it is way more efficient to say
std::advance(it, l.size()/2);
l.insert(it, value);
Unfortunately, l.insert(l.begin + (l.size()/2), value) won't work because list iterators aren't random access, therefore don't have operator+ defined (to prevent performance surprises!). Keep in mind that std::advance() might be a costly operation depending on iterator type (it will be slow for reverse iterators implemented on a forward-only container, e.g.).
Here, "the middle" means an arbitrary point in the list, as long as you already have an iterator referring to that point. Given that, insertion is just a matter of modifying a few pointers around the insertion point.
If you don't already have an iterator for the insertion point, and it isn't somewhere simple like the beginning or the end, then it will take linear time to walk through the list to find it.
When we say 'inserting in the middle' do we really mean that we save linear time to go from the beginning of the list to the desired point ( traversing one by one through all linked elements in between)?
Yes.
Basically, It means the list needs to just change pointers to insert a new element and not navigate the entire list or copy the contents etc.Hence the insertion is constant time, because there is no need of traversing the list or copying the containers etc.
Inserting in the "middle" of a list means inserting somewhere other than the beginning or end. But doing an insertion requires an iterator to the insertion point. Once you have such an iterator, the insertion time is constant, but getting such an iterator in the first place is a separate issue.
No, when we say "inserting in the middle" we do not mean "finding the insertion point according to whatever criteria that takes traversing of the whole or indefinite part of the list".
What exactly are iterators in the C++ STL?
In my case, I'm using a list, and I don't understand why you have to make an iterator std::list <int>::const_iterator iElementLocator; to display contents of the list by the derefrence operator:
cout << *iElementLocator; after assigning it to maybe list.begin().
Please explain what exactly an iterator is and why I have to dereference or use it.
There are three building blocks in the STL:
Containers
Algorithms
Iterators
At the conceptual level containers hold data. That by itself isn't very useful, because you want to do something with the data; you want to operate on it, manipulate it, query it, play with it. Algorithms do exactly that. But algorithms don't hold data, they have no data -- they need a container for this task. Give a container to an algorithm and you have an action going on.
The only problem left to solve is how does an algorithm traverse a container, from a technical point of view. Technically a container can be a linked list, or it can be an array, or a binary tree, or any other data structure that can hold data. But traversing an array is done differently than traversing a binary tree. Even though conceptually all an algorithm wants is to "get" one element at a time from a container, and then work on that element, the operation of getting the next element from a container is technically very container-specific.
It appears as if you'd need to write the same algorithm for each container, so that each version of the algorithm has the correct code for traversing the container. But there's a better solution: ask the container to return an object that can traverse over the container. The object would have an interface algorithms know. When an algorithm asks the object to "get the next element" the object would comply. Because the object came directly from the container it knows how to access the container's data. And because the object has an interface the algorithm knows, we need not duplicate an algorithm for each container.
This is the iterator.
The iterator here glues the algorithm to the container, without coupling the two. An iterator is coupled to a container, and an algorithm is coupled to the iterator's interface. The source of the magic here is really template programming. Consider the standard copy() algorithm:
template<class In, class Out>
Out copy(In first, In last, Out res)
{
while( first != last ) {
*res = *first;
++first;
++res;
}
return res;
}
The copy() algorithm takes as parameters two iterators templated on the type In and one iterator of type Out. It copies the elements starting at position first and ending just before position last, into res. The algorithm knows that to get the next element it needs to say ++first or ++res. It knows that to read an element it needs to say x = *first and to write an element it needs to say *res = x. That's part of the interface algorithms assume and iterators commit to. If by mistake an iterator doesn't comply with the interface then the compiler would emit an error for calling a function over type In or Out, when the type doesn't define the function.
I'm being lazy. So I would not type describing what an iterator is and how they're used, especially when there're already lots of articles online that you can read yourself.
Here are few that I can quote for a start, proividing the links to complete articles:
MSDN says,
Iterators are a generalization of
pointers, abstracting from their
requirements in a way that allows a
C++ program to work with different
data structures in a uniform manner.
Iterators act as intermediaries
between containers and generic
algorithms. Instead of operating on
specific data types, algorithms are
defined to operate on a range
specified by a type of iterator. Any
data structure that satisfies the
requirements of the iterator may then
be operated on by the algorithm. There
are five types or categories of
iterator [...]
By the way, it seems the MSDN has taken the text in bold from C++ Standard itself, specifically from the section §24.1/1 which says
Iterators are a generalization of
pointers that allow a C + + program to
work with different data structures
(containers) in a uniform manner. To
be able to construct template
algorithms that work correctly and
efficiently on different types of data
structures, the library formalizes not
just the interfaces but also the
semantics and complexity assumptions
of iterators. All iterators i support
the expression *i, resulting in a
value of some class, enumeration, or
built-in type T, called the value type
of the iterator. All iterators i for
which the expression (*i).m is
well-defined, support the expression
i->m with the same semantics as
(*i).m. For every iterator type X for
which equality is defined, there is a
corresponding signed integral type
called the difference type of the
iterator.
cplusplus says,
In C++, 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 (at
least, the increment (++) and
dereference (*) operators).
The most obvious form of iterator is a
pointer [...]
And you can also read these:
What Is an Iterator?
Iterators in the Standard C++ Library
Iterator (at wiki entry)
Have patience and read all these. Hopefully, you will have some idea what an iterator is, in C++. Learning C++ requires patience and time.
An iterator is not the same as the container itself. The iterator refers to a single item in the container, as well as providing ways to reach other items.
Consider designing your own container without iterators. It could have a size function to obtain the number of items it contains, and could overload the [] operator to allow you to get or set an item by its position.
But "random access" of that kind is not easy to implement efficiently on some kinds of container. If you obtain the millionth item: c[1000000] and the container internally uses a linked list, it will have to scan through a million items to find the one you want.
You might instead decide to allow the collection to remember a "current" item. It could have functions like start and more and next to allow you to loop through the contents:
c.start();
while (c.more())
{
item_t item = c.next();
// use the item somehow
}
But this puts the "iteration state" inside the container. This is a serious limitation. What if you wanted to compare each item in the container with every other item? That requires two nested loops, both iterating through all the items. If the container itself stores the position of the iteration, you have no way to nest two such iterations - the inner loop will destroy the working of the outer loop.
So iterators are an independent copy of an iteration state. You can begin an iteration:
container_t::iterator i = c.begin();
That iterator, i, is a separate object that represents a position within the container. You can fetch whatever is stored at that position:
item_t item = *i;
You can move to the next item:
i++;
With some iterators you can skip forward several items:
i += 1000;
Or obtain an item at some position relative to the position identified by the iterator:
item_t item = i[1000];
And with some iterators you can move backwards.
And you can discover if you've reached beyond the contents of the container by comparing the iterator to end:
while (i != c.end())
You can think of end as returning an iterator that represents a position that is one beyond the last position in the container.
An important point to be aware of with iterators (and in C++ generally) is that they can become invalid. This usually happens for example if you empty a container: any iterators pointing to positions in that container have now become invalid. In that state, most operations on them are undefined - anything could happen!
An iterator is to an STL container what a pointer is to an array. You can think of them as pointer objects to STL containers. As pointers, you will be able to use them with the pointer notation (e.g. *iElementLocator, iElementLocator++). As objects, they will have their own attributes and methods (http://www.cplusplus.com/reference/std/iterator).
There already exists a lot of good explanations of iterators. Just google it.
One example.
If there is something specific you don't understand come back and ask.
I'd suggest reading about operator overloading in C++. This will tell why * and -> can mean essentially anything. Only then you should read about the iterators. Otherwise it might appear very confusing.