I am making my own STL-like container - tree with any number of children
template<typename Type>
class Node
{
Type value;
Iterator AddChild(const Type & value);
void Remove(const Iterator & where);
...
};
I decided that iterator's operator* should return value of current node, but what should return operator->? Currently, it returns Node<Type>* and it very useful in such situations
Node<int>::Iterator it = tree.begin();
it->AddChild(4);
But my mentor said me, that operator-> should return Type*. What is STL-like way to access Node's methods? Something like it.Ref().MyMethod() doesn't look good.
Your mentor is right, the return type of operator->() should be Type*.
The big idea behind iterators is that they are just smart pointers to some locations inside the container. You can alter the value stored at that location (by assigning to *it) or access its members, but to do more drastic (i.e. structural) changes to container contents, you have to have direct access to the container itself.
That's why in STL, there are no node's methods. Instead, there're container's methods (and also algorithms) that accept iterators as arguments.
In other words, the STL way of doing what you're trying to do is:
Node<int>::Iterator it = tree.begin();
tree.AddChild(it, 4);
operator-> should return YourTree::value_type* and operator* should return YourTree::value_type&. (Actually a YourTree::pointer and YourTree::reference, but these are normally just aliases for * and & of the value type). Note the consistency. Without it, the standard algorithms will not work.
It is up to you to decide what the value_type is. It could well be Node if you want. This however can be confusing and hard to implement consistently. I would leave it as Type.
The programmer expects it->method to be equivalent to (*it).method, so the operator-> should return pointer to the same thing that operator* returns reference to. Normally that should be the value of the iterator, because that's the expected way to get at the value.
You can expose methods of the node as methods of the pointer, i.e. called as it.method, but it's somewhat confusing and in most cases it would require extra data in the iterator compared to methods of the container taking iterator as argument. That's why STL always uses methods of the container taking iterator. E.g. container.insert(iterator, value) inserts value after iterator.
Related
When we use iterator we declare iterator and then itr as an object, but we don't pass any pointer like we do every time when declaring pointer variable but when we print the value of vector by the use of iterator than how itr became*itr
when we doesn't pass any pointer
Is pointer is hidden or its work on the background?
Example like:
iterator itr;
*itr
How it works does * means any other things to iterator or *itr act like normal pointer variable.
If it works like a pointer variable then why we do not pass * when declaring itr.
An iterator is an object that lets you travel (or iterate) over each object in a collection or stream. It is a sort of generalization of pointers. That is, pointers are one example of an iterator.
Iterators implement concepts required by various algorithms such as forward iteration (meaning it can be incremented to move forward in the collection), bi-directional iteration (meaning it can go forward and backward), and random access (meaning you can use an index an arbitrary item in the collection).
For instance, moving backward can't typically happen in a stream, so stream's iterators are typically forward iterators only because once you access a value, you can't go back in the stream. A linked list's iterators are bi-directional because you can move forward or backward, but you cannot access them by indexing because the nodes are not typically in contiguous memory, so you can't calculate with an index where an arbitrary element is. A vector's iterators are random access and very much like pointers. (C++20 made these categories more precise, so the old categories are now called "Legacy".)
Iterators can also have special functions, such as std::back_inserter, which appends items to the end of a container when a value is assigned to it's referrent.
So, you can see that iterators allow you to be more precise in defining what your consumer of iterators expects. If your algorithm requires bi-directional iteration, you can communicate that and limit it so it won't work with forward-only iterators.
As for the * operator, it is similar to the * operator for a pointer. In both cases, it means, "give me the value referred to by this handle". It is implemented via operator overloading. You do not need the * when declaring an iterator because it is not a pointer, which is a lower-level construct in the language. Rather, it is an object with pointer-like semantics.
To answer your questions below:
No, the * is not automatically created. When you declare an iterator you are declaring an object. When the class for that object is defined, it may or may not have an operator overload for the * operator (or the == or the + or any other operators).
When you go to use the object, such as passing it to a function, the types will need to match up. If a function you were passing it to requires an iterator (e.g. std::sort()), then no dereferencing * is needed. If the function was expecting a value of the type the iterator refers to, then you would need to dereference it first. In that case the compiler calls the overloaded operator *and returns the value.
That is the nature of overloaded operators -- they look like ordinary operators but ultimately are resolved to a function call defined by the creator of the class. It works the same as if you defined a matrix class that has plus and minus operators overloaded.
How it works does * means any other things to iterator or *itr act like normal pointer variable.
It depends what type stands behind iterator. It can be alias for a pointer:
using iterator = int *;
iterator itr;
*itr; // it is pointer dereferencing in this case.
Or it can be a user defined type:
struct iterator {
int &operator*();
};
iterator itr;
*itr; // it means itr.operator*() here
So without knowing what type iterator is it is quite impossible to say what * actually does here. But in reality you should not care as developers of the library should implement it the way it would not matter for you.
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.
The C++11 standard has changed the signature of the erase() methods of standard containers: they now accept const_iterators instead of iterators. The rationale is explained in this document:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2350.pdf
Now, if one implements std::vector<T> in a straightforward way, it is possible to use directly const T * and T * as const and mutable iterator types respectively. So in the erase() method we might have code like this:
iterator erase(const_iterator it)
{
...
for (; it != end() - 1; ++it) {
// Destroy the current element.
it->~T();
// Move-init the current iterator content with the next element.
::new (static_cast<void *>(it)) T(std::move(*(it + 1))); // FAIL
}
...
}
The problem now is that, since it is a const pointer, the static cast will fail.
Is it legal in this case to cast away the constness from it? Note that it never points to a const object (the objects stored in the vector are never const), and that the calling method (erase()) is not const as well.
EDIT: thanks for all the replies. I wanted to respond here to some comments below.
This code comes from a custom vector class (with a similar interface to std::vector) that implements the small buffer optimisation on top of an unrestricted union. The iterators are naked pointers because they need to be the same type both when the vector is using static storage and when it is using dynamic storage, and this seems the most natural way of achieving such result.
The cast to void * is just a matter of habit and consistency within the codebase when interacting with unitialised storage.
Since erase is non-const, yes, you can safely cast away const on the element. However, note that this is not necessary, since a non-const iterator can be obtained from the const iterator:
iterator non_const = begin() + (it - begin());
And that can be used to iterate over the vector.
when creating an iterator of a vector, the iterator itself is a pointer to the values held by the vector. therefore *iterator is actually the value held by the vector.
so I have two questions:
when using an iterator on a map, what is the iterator actually? I mean, what is it's inner implementation? is it like a struct that holds different data members?
If I want to implement my own iterator, which holds several data members, what am I actually returning when creating an iterator?
Depends on implementation. Usually, std::map is implemented as a balanced binary search tree. In that case, the iterator would likely point to a node in the tree.
The iterator of a std::map is a structure that references the key-value-pairs saved in your map. The standard iterator which you get with i.e begin() or end() is a bidirectional iterator. This means that your can call ++i and --i operators on the iterator object to move for/backward between the items saved in your map.
Why do you want to implement your own iterator? Maybe creating a class or struct saving it to a std::vector<T> will do what you want?! you can access the iterator by std::vector<T>::iterator. If you really want to implement your own iterator, you should ask yourself the question if it should work for your own data structures as a test, or if you want to be compatible with i.e. std data structures. If its the latter, you should derive from a iterator implementation and modify it the way of your needs. Look at this answer as well.
The iterator itself is a pointer to the values held by the vector.
Vector iterator is not a pointer to value but a class with operator * implemented, which returns the value held by the container and pointed out by your iterator. In case of map you can access key and value using first and second fields:
map<string, int> wheelMap;
wheelMap["Car"] = 4;
map<string, int>::iterator itWheel = wheelMap.begin();
cout << itWheel ->first << ":" << itWheel ->second << endl; //This will print: Car:4
Map iterator has also other operators implemented: +, ++, -, --, ->, ==, !=.
Additionally vector has also the operator [] implemented to get the values by index.
I implemented std::map-like container as red-black-tree (like often sited being used for std::map implementation) and only thing the iterator implementation needs (both const and non-const versions) is pointer to the tree node. Each tree node contains pointers to the two children and parent (plus color bit) which is enough to traverse the tree to either direction. In general it depends on the container & iterator types (and implementation) though what kind of data is needed to implement its functionality. E.g. my deque iterators have pointer to the container and index of the element, but it's really implementation specific how the iterators are implemented and what data they need.
Given you have an iterator that is effectively a proxy and contains the data that it returns, how can you make a reverse iterator?
std::reverse_iterator implementation of the dereferencing operator creates a temporary object which it then decrements and dereferences, with code that is something like:
reference operator*() const {
iterator tmp = current;
return *--tmp;
}
with the result that what it returns to you is a pointer to data that goes out of scope before you get hold of it.
This has rather unfortunate results.
How can you get round this?
It looks like you'll need to write your own custom reverse iterator implementation for this specific case since your iterator type is not compatible with this specific implementation of reverse_iterator.
According to http://en.cppreference.com/w/cpp/iterator/reverse_iterator, some implementations do also store a decremented copy of the iterator but not all. Boost::reverse_iterator does not appear to store an additional copy.
I think the standard library implementation is incorrect. If you look at 24.5.1.3.4 [reverse.iter.op.star] in the C++11 standard you'll find the following:
deref_tmp = current;
--deref_tmp;
return *deref_tmp;
Note: This operation must use an auxillary member variable rather than a temporary variable to avoid returning a reference that persists beyond the lifetime of the associated iterator.
In the standard deref_tmp is a for-exposition-only data member of reverse_iterator.