begin() vs rbegin() how they are implemented? - c++

In the following image I'm using the same type of iterators which means the same implementation of operator++ so how does the compiler know if it should get the next value or the previous one...?

First, the picture you have contains an error. As you can see here the reverse iterator type of vector is std::reverse_iterator<iterator> which uses the template std::reverse_iterator<T>. So begin and rbegin have not the same return value (and I also do not think they are convertible to each other).
But actually that wouldn't even be a problem, you could implement
struct iter {
bool reverse;
pointer ptr;
iter& operator++() {
if(reverse) --ptr;
else ++ptr;
return *this;
}
};
But type-seperating them is really preferable.

Related

How to overload an iterator so it can be incremented in a dereferenceable state?

to give context i'm trying to re-implement vector container, and while reading about it's iterator system requirements, i've stumbled upon this requirement from cplusplus reference:
can be incremented (if in a dereferenceable state ). the result is either dereferenceable or past-the-end iterator.
EX: ++a , a++, *a++
i've already overloaded the increment and dereference operators as so:
class iterator {
...
inline iterator& operator++() {
++_ptr;
return *this;
};
inline iterator operator++(int) {
iterator tmp(*this);
operator++();
return (tmp);
};
inline reference operator*() const {
return _ptr;
};
...
}
i've researched a bit about this requirement and didn't found any informations mentioning this property, so i assumed it's irrelevant and handled by default by the compiler as long as i overload the operators separately, is that the case or should i implement my iterator in a way so that it can be incremented in a dereferenceable state.
You cannot increment an end iterator, it is not dereferenceable. You cannot increment an invalid iterator, it is not dereferenceable.
Preconditions do not necessarily mean that you have to do something extra. They only give information when some operation, here increment, is defined.
For example
std::vector<int> foo;
auto it = foo.end();
++it; // undefined
Incrementing the end iterator is undefined.
When a user calls your operator++ on an invalid iterator (and then try to dereference it) it is their fault. They broke the contract.
For analogy consider a function:
int div(int a,int b) { return a/b; }
And specification states: "You can only call the function with b!=0". The function does not need to check b!=0, because if a user calls it with b==0 they broke the contract.
In a debug build you might want to add some convenience and check if preconditions hold before actually incrementing the iterator. Though in a release build this would incur unnecessary cost and be a nuisance for anybody who only increments dereferencable iterators (as they should).

Why does my templated function require a conversion from one iterator to the other?

I am trying to implement a binary search tree container. At the moment I have to implement a find() function that is able to return an iterator or a constant iterator. I choose to overload the find function to accomodate both possibilities
MyIterator<treepair> find(const key& x)
{
return tree_search<MyIterator<treepair>>(root,x);
}
const_MyIterator<treepair> find(const key& x) const
{
return tree_search<const_MyIterator<treepair>>(root,x);
}
Then the function tree_search recursively finds the node that contains the wanted key by percurring the tree:
template<typename iterator>
iterator tree_search(Node<treepair>* x, const key& y) const
{
if(x == nullptr)
{
std::cout<<"element not found"<<std::endl;
iterator x = end();//HERE I HAVE A PROBLEM
return x;
}
else if (y == x->value.first)
{
iterator i{x,tree_maximum()};
std::cout<<"element found"<<std::endl;
return i;
}
if(y < x->value.first)
return tree_search<iterator>(x->left,y);
else return tree_search<iterator>(x->right,y);
}
Now the end() function is overloaded to give both a const_iterator and a regular iterator:
MyIterator<treepair> end(){
return MyIterator<treepair>{nullptr,tree_maximum()};
}
const_MyIterator<treepair> end() const{
return const_MyIterator<treepair>{nullptr,tree_maximum()};
}
however I receive this error
test_on_iterators.cc:508:12: error: conversion from ‘const_MyIterator<std::pair<int, int> >’ to non-scalar type ‘MyIterator<std::pair<int, int> >’ requested
iterator x = end();
Is this error due to a requirement in the conversion between the types? Isn't the compiler supposed to choose the wanted end() function according to the iterator type it has to produce?
Isn't the compiler supposed to choose the wanted end() function according to the iterator type it has to produce?
No.
tree_search() is a const method. That means its this pointer is pointing at a const object (even if the object tree_search() is called on is not really const).
As such, when tree_search() internally calls end(), it calls the overload that is callable on a const object. That overload returns a const_MyIterator. Then tree_search() tries to assign that const_MyIterator to a non-const MyIterator, which is where you get the error since there is no conversion defined from const_MyIterator to MyIterator.
You need to make x be a const_MyIterator to match what the const version of end() returns.
You should also make tree_search() return a const_MyIterator as well, instead of returning a non-const iterator. If you want tree_search() to return a non-const Iterator, then don't declare it as a const method.

How does begin() know which return type to return (const or non-const)?

This works perfectly :
list<int> l;
list<int>::const_iterator it;
it = l.begin();
list<int>::iterator it2;
it2 = l.begin();
What I don't get is how the list "knows" that it must return the iterator begin() version or the const_iterator begin() const one.
I'm trying to implement iterators for my container (a trie) and I'm running into this problem. Isn't C++ supposed not to handle differentiation by return type (except when using weird tricks)?
Here is some code and the compiler error I get :
My Trie<T> is a templated trie that can contain any type. I have a Trie<int>::iter non-const iterator and a Trie<int>::const_iter const iterator. iter begin() and const_iter begin() const are declared (and defined) in the Trie class.
Trie<int> t;
Trie<int>::const_iter it;
it = t.begin();
Error :
../test/trie.cpp:181: error: no match for 'operator=' in 'it = Trie<T>::begin() [with T = int]()'
[..path..]/Trie.h:230: note: candidates are: Trie<int>::const_trie_iterator& Trie<int>::const_trie_iterator::operator=(const Trie<int>::const_trie_iterator&)
So, I believe the non-const version of begin is not used.
I contemplated creating an operator=(const Trie<T>::const_trie_iterator&) method for the non-const iterator but I don't see that in the STD lib and I would have to const_cast the iterator. What should I do?
In standard containers, a non-const iterator is implicitly convertible to a const_iterator. The type returned is based solely on the const-ness of the object/reference on which begin() was called, which in your case would be iterator, there is a conversion that allows the later assignment.
In particular in the 23.2.1 General Container Requirements, table 96, it says that X::iterator must be convertible to X::const_iterator.
list knows which kind of iterator to return because there are two begin methods defined, one for when the list is const, and one for when it isn't. The declarations might look something like this:
template<class T>
class list {
public:
iterator<T> begin();
const_iterator<T> begin() const;
}
In the following example, the first, non-const iterator would be returned, because the list isn't const:
void doSomething(list<int> &myList) {
iterator<int> i = myList.begin();
...
}
In the next example, the list is declared as const, so the second version of begin that returns a const_iterator would be used instead:
void doSomethingElse(const list<int> &myList) {
const_iterator<int> i = myList.begin();
....
}
Of course, an iterator can always be cast to a const_iterator, so you could declare i to be a const_iterator in either example, but if you try to declare i to be an iterator in the second example you'll get an error since a const_iterator can not be implicitly cast as an iterator.

implementation of string class

I'm interested in the implementation of the reverse iterator with rbegin(), rend() and operator++ of the class string, I can't find it in google, how can I do it? thanks in advance for any help or any link
You could look in the implementation header files. (e.g. /usr/include/c++/4.1.2/string on Linux). This will typically just pull in a load of other headers where the real meat lies, such as bits/basic_string.h.
I don't know where they reside for e.g. VC++, but you can usually just get Intellisense to find it by creating a std::string, selecting it and pressing F12.
There is a basic implementation of reverse_iterator in the STL.
It is templatized by the Iterator to be reverted.
The idea is quite simple, if you look at a range:
[first, second, .... last]
^ ^
begin end
rend rbegin
There is extra-work done, compared to a pure reverse iterator using this implementation, since for each dereference, you need to copy the iterator you hold, decrement it, and then dereference it.
Reverse iteration for bidirectional iterators is implemented in the std::reverse_iterator template.
The implementation of reverse iterators for std::string doesn't take more than:
template <xxx>
class basic_string
{
public:
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
reverse_iterator rbegin() { return reverse_iterator(this->end()); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(this->end()); }
reverse_iterator rend() { return reverse_iterator(this->begin()); }
const_reverse_iterator rend() const { return const_reverse_iterator(this->begin()); }
//...
};
(Copied from GCC's implementation.)

How is insert iterator work in c++

there is insert iterator in database template library or other library, Can someone tell me how it work ?
Thanks!
It is a template class so you should be able to look it up in the implementation.
However, the idea is that it stores an iterator (current location) and a reference (pointer) to a container (that is being inserted in). Then it overloads operator= like this:
insert_iterator& operator= (typename Container::const_reference value)
{
m_iter = m_container->insert(m_iter, value);
++m_iter;
return *this;
}
So it requires a container that supports the insert method and at least a forward iterator, and has the standard typedefs (const_reference or perhaps value_type), so it can declare the right-hand type of its operator=.
The other output iterator operators (*, ++) just return *this.