Semantic of -> operator in lists (and in general C++) - c++

My current assignment is writing a list with iterators. The list isn't being a problem so much as creating the iterator class is.
From a couple of sources I've seen that I have two operators to define in my iterator class: operator* and operator->.
Great so far! Supposing my iterator structure is so
// Nested class of List
class _Iter
{
private:
ListElem *pCurr;
const List *pList;
public:
_Iter(ListElem *pCurr, const List *list)
: pCurr_(pCurr), pList(list)
{}
T& operator*() { return pCurr_->data; }
T* operator->() { return &**this; }
};
with ListElem being
// Nested struct of List
struct ListElem
{
T data;
ListElem *next;
ListElem *prev;
};
I can see I'm doing something massively wrong (as double dereferencing of this would lead to a &(*pCurr_->data), which is not dereferencable.
My main problem is not understanding what -> is actually supposed to do in this case. Should it grant the user access to the ListElem class? If that's the case, why can't I just write
ListElem *operator->() { return pCurr_; }
instead of returning a pointer? My understanding of these two operators as used in my list (and hopefully STL lists) is that:
operator*() // Return data pointed to by iterator; pCurr_->data;
operator->() // Grant access to data-holding structure; pCurr;
Is this correct, or what am I not getting? (And does -> have a proper name?)

Whatever you do, (*something).somethingElse should be equivalent to something->somethingElse. The latter is just a short syntax for the former. Therefore,
T& operator*() { return pCurr_->data; }
T* operator->() { return &**this; }
is fine because *this just dereferences this which has the type _Iter*, not _Iter, so no operator*() call is done. Then you dereference *this, so you get pCurr->data, then you take its address, so you get &pCurr->data. But it would be much clearer to just write:
T& operator*() { return pCurr_->data; }
T* operator->() { return &pCurr->data; }
Now, this
ListElem *operator->() { return pCurr_; }
is wrong because if operator*() returns T&, operator->() should return T*, that's what it was designed for. If you really want to grant access to ListItem instead of its data (which may or may not make sense depending on the design, but in your case it looks like it doesn't), then you should also redefine operator*() to get this:
ListElem& operator*() { return *pCurr_; }
ListElem *operator->() { return pCurr_; }
Note that it is not a language requirement, it is just how you design your class to avoid confusing interface.

Your main guideline should be that
(*iter).hello();
iter->hello();
should both do the same thing. That's what the user expects. Returning ListElem gives nothing to the user. The user shouldn't even know the details of the implementation of ListElem.

operator-> gives a pointer to the object pointed to by the iterator, in this case (apparently) pCurr_->data.
T *operator->() { return &(pCurr_->data); }
It should return the address of the object returned by operator*() as a value or reference.
T &operator*() { return pCurr_->data; }
// or
T &operator*() { return *operator->(); }
operator->() exists to implement -> with iterators (with the behavior it has for pointers) and is necessary because operator* may return an object by value instead of by reference.
Note that you don't need to store a pointer to the List in the iterator to obtain the functionality required.

Related

C++: "Iterable<T>" interface

What I want to achieve is probably easily explained: Consider I have an abstract class that I know will contain multiple objects of known type. However the actual container holding these objects will be implemented in sub-classes.
In my abstract base class I now want to provide an interface to iterate over these objects. Given that I don't know (or rather don't want to fix) the type of container, I thought that iterators would probably be my best bet.
A conceptual declaration of this class might look like this:
class MyClass {
public:
// Other interface methods, e.g. size()
virtual Iterable<MyObject> objects() = 0;
};
The intention here is that I'll be able to iterate over the nested objects of my class like this:
MyClass *class = new ImplementationOfClass();
for (const MyObject &obj : class->objects()) {
// Do stuff with obj
}
The issue I am facing however is that I can't seem to figure out how Iterable<MyObject> should be defined. The key property of this object is that at the time of defining this class I can only specify that the returned value will be iterable (using STL-style iterators) and will yield objects of type MyObject when the used iterator is dereferenced.
Normally I would use an abstract class on its own for this but it seems that this is very tricky (impossible?) since iterators are always passed by value and thus to my knowledge no Polymorphism is possible.
Questions dealing with how to pass arbitrary iterator types as arguments into a function always come up with the "use templates" answer. However I think in my case I can't use templates for that. This assumption might be wrong though, so feel free to correct me.
Essentially the barrier I always run into is that at some point I have to write down the iterator type explicitly which in my case I can't. I thought about using a template for that but this would then inhibit proper Polymorphism (I think?) because the user of that abstract interface seems to have the burden of explicitly initializing the correct template. The whole point of all of this however is that the caller does not have to care about the underlying structure.
TL;DR: Is there a way to create an interface class that only promises to be iterable and that dereferencing an iterator will yield an object of type T?
With the help of #FrançoisAndrieux and a hint from https://stackoverflow.com/a/4247445/3907364, I was able to come up with an approach to my problem.
Essentially the idea is to create an iterator-wrapper that stores a function to obtain an object of the given type if given an index. That index is then what is iterated on.
The nice thing about this is that the iterator interface is fixed by specifying the type of object that dereferencing it should return. The polymorphism comes into play by making the member function objects() virtual so that each sub-class can construct the iterator itself, providing a custom function pointer. Thus as long as there is a way to map an index to the respective element in the container (whichever is used), this trick is usable.
Note that you can either directly use pointers to e.g.std::vector<T>::at or create a custom function that will return the respective element.
Here's the implementation for the iterator (The implementation could probably be improved upon but it seems to get the job done):
template< typename T > struct iterator_impl {
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T *;
using reference = T &;
using access_function_t = std::function< T &(std::size_t) >;
// regular Ctor
iterator_impl(std::size_t start, access_function_t &func, const void *id)
: m_index(start), m_func(func), m_id(id) {}
// function-move Ctor
iterator_impl(std::size_t start, access_function_t &&func, const void *id)
: m_index(start), m_func(func), m_id(id) {}
// copy Ctor
iterator_impl(const iterator_impl &) = default;
// move ctor
iterator_impl(iterator_impl &&other) {
std::swap(m_index, other.m_index);
m_func = std::move(other.m_func);
std::swap(m_id, other.m_id);
}
// copy-assignment
iterator_impl &operator=(const iterator_impl &other) = default;
// prefix-increment
iterator_impl &operator++() {
++m_index;
return *this;
}
// postfix-increment
iterator_impl operator++(int) {
iterator_impl old = *this;
++(*this);
return old;
}
bool operator==(const iterator_impl &other) { return m_index == other.m_index && m_id == other.m_id; }
bool operator!=(const iterator_impl &other) { return !(*this == other); }
T &operator*() { return m_func(m_index); }
T *operator->() { return &m_func(m_index); };
protected:
std::size_t m_index = 0;
access_function_t m_func;
const void *m_id = nullptr;
};
Note that I had to introduce the m_id member variable as a means to properly compare iterators (std::function can't be compared using ==). it is meant to be e.g. the address of the container the elements are contained in. Its sole purpose is to make sure that 2 iterators that happen to have the same index but are iterating over completely different sets are not considered equal.
And based on that here's an implementation of an Iterable<T>:
template< typename T > struct Iterable {
using iterator = iterator_impl< T >;
using const_iterator = iterator_impl< const std::remove_const_t< T > >;
Iterable(std::size_t start, std::size_t end, typename iterator_impl< T >::access_function_t &func, const void *id)
: m_begin(start, func, id), m_end(end, func, id) {}
iterator begin() { return m_begin; }
iterator end() { return m_end; }
const_iterator begin() const { return m_begin; }
const_iterator end() const { return m_end; }
const_iterator cbegin() const { return m_begin; }
const_iterator cend() const { return m_end; }
protected:
iterator m_begin;
iterator m_end;
};

returning a iterator by reference

I want to access my iterator class by reference
#include <iostream>
template <typename T> class binary_tree;
template <typename T>
class binary_tree_iterator {
private:
binary_tree<T>* tree;
T data;
public:
binary_tree_iterator(binary_tree<T>* t) : tree(t) {}
T& operator*() {data = tree->data(); return data;}
binary_tree_iterator& operator++() {tree = tree->get_node(); return *this;}
bool operator!=(binary_tree_iterator& rhs) {return tree->data() != rhs.tree->data();}
};
template <typename T>
class binary_tree {
private:
T t_data;
binary_tree<T>* node;
binary_tree_iterator<T>* It;
public:
binary_tree(T d) : t_data(d), node(nullptr), It(nullptr)
{}
T& data() {
return t_data;
}
void set_node(binary_tree<T>* node) {
this->node = node;
}
binary_tree<T>* get_node() {
return node;
}
binary_tree_iterator<T> begin() {
It = new binary_tree_iterator<T>(this);
return *It;
}
binary_tree_iterator<T> end() {
if(node == nullptr) {
It = new binary_tree_iterator<T>(this);
return *It;
} else {
return node->end();
}
}
};
int main() {
binary_tree<int>* tree = new binary_tree<int>(2);
tree->set_node(new binary_tree<int>(3));
//for(auto& x: *tree) <--- does not work
for(auto x: *tree) {
std::cout << x << std::endl;
}
}
The for-range loop I want to use it in looks something like for(auto& x: *tree). How do I give it a reference? Is there a standard way of doing this when creating iterators? When I return the data value I assign it to a iterator data member so I can return by reference. Will I have to do the same with my iterator? I don't imagine this is the standard way of doing this.
I want to access my iterator class by reference
How do I give it a reference?
By changing the return type of the function to be binary_tree_iterator<T>& instead of binary_tree_iterator<T>. If you do that, you'd have to store an iterator somewhere instead of returning a new one, so that you can refer to it. Presumably, it would have to be stored as a member variable.
Is there a standard way of doing this when creating iterators?
No. None of the standard containers return references to iterators.
I don't imagine this is the standard way of doing this.
Indeed. The "standard" i.e. conventional thing to do is to not return references to iterators.
The for-range loop I want to use it in looks something like for(auto& x: *tree)
There is no need to return a reference to an iterator in order to make that work. If you take a look at standard containers, you'll find that none of them return a reference to an iterator, and such loop works with all of them.
The reference in that loop is bound to the result of indirection through the iterator. So, it is the operator* that must return a reference to the pointed object. And, your operator* does indeed return a reference. That said, normally an iterator would return a reference to the object stored in the container; not a reference to copy stored in the iterator. So, that's highly unconventional.
Finish writing your iterator, and you'll find that the loop works.
In conclusion: You don't need to return iterator by reference, and you shouldn't.

How to access standard *this behaviour after overloading operator*?

I'm writing a wrapper for an iterator, and trying to make it behave just like the contained iterator.
For this, it requires to overload both operator* and operator++, but this poses the following problem:
//Typedefs to make below code easier to read
typedef std::map::const_iterator<std::string, SomeClass> iteratorAlias;
typedef std::pair<const std::string, SomeClass> mapPairAlias;
class IteratorWrapper
{
iteratorAlias iterator;
/*Other code omitted*/
mapPairAlias& operator*()
{
return *iterator;
}
const mapPairAlias& operator*() const
{
return *iterator;
}
IteratorWrapper& operator++()
{
++this->iterator;
return *this;
}
IteratorWrapper operator++(int)
{
IteratorWrapper copy(*this);
++(*this);
return copy;
}
};
In both operator++ functions, this needs to be dereferenced. But it looks like that would result in getting the mapPairAlias from the overloaded function, in stead of the IteratorWrapper.
How should this issue be resolved? The STL iterators handle both functions just fine, so presumably there is a way.
this is a raw pointer to the current instance
*this is a reference to the current instance
**this is syntactic sugar for this->operator*()
So the issue you described does not exist, *this will do what you want just fine.

std::shared_ptr Iterator

I have an iterator which iterates over a std::shared_ptr. So operator++ points the internally stored shared pointer to the next item.
template<class IT>
class MyIterator
{
public:
...
MyIterator& operator++()
{
_i = ... // Call factory
return *this;
}
private:
std::shared_ptr<IT> _i;
};
How should I implement the operator*() and operator->() operators?
How should I test if the iterator is pointing to NULL, i.e. if the internal shared pointer is pointing to NULL.
You should define under what circumstances users are permitted to deference an instance of your class. Usually this is "anything other than an end iterator or an uninitialized iterator".
Then you should ensure that _i never contains a null pointer for an instance that can be dereferenced.
This means there is no need for a check, because the user is not permitted to call operator* or operator-> in that circumstance. You could add one for debugging, for example: assert(_i.get());.
You don't specify what the value_type is of your iterator, but assuming that it is IT, you can implement:
IT &operator*() { return *_i; }
shared_ptr<IT> operator->() { return _i; }
// or
IT *operator->() { return _i.get(); }

How to implement operator-> for iterator type?

Is there a way to implement operator->, not only operator*. To have following code working:
Iterator<value> it = ...
i = (*it).get();
i = it->get(); // also works
Let we say that value type has method get.
When Iterator is implemnted as below:
template<T> class Iterator {
T operator*() { return ... }
T operator->() { return ... }
}
Here ... is an implementation of getting right T object.
Somehow it won't work when I implement it in this way. I think I misunderstand something.
operator-> should return a pointer:
T * operator->();
T const * operator->() const;
operator* should return a reference if you want to use it for modification:
T & operator*();
T operator*() const; // OR T const & operator*() const;
As odd as this may seem, you want to return a pointer to T, thusly:
T * operator->() { return &the_value; }
Or a pointer to const.
You don't specify what "it won't work" means - does it fail to compile, or does something else than expected? I will assume it compiles, because from your snippet I can't see why it shouldn't.
What you are doing is returning by value. So you return a new instance of the pointed-to object. You instead should return a pointer in operator-> and reference in operator*