Suppose that I have a following class
template <typename T>
struct Node { T value; Node* next; };
Often one needs to write code similar to this (let's assume that Sometype is std::string for now, although I don't think that it matters).
Node<SomeType> node = Node{ someValue, someNodePtr };
...
Node <const SomeType> constNode = node; // compile error
One way to work around is to define explicit conversion operator:
template <typename T>
struct Node
{
T value;
Node* next;
operator Node<const T>() const {
return Node<const T>{value, reinterpret_cast<Node<const T>* >(next)};
}
};
Is there a better, "proper" way to do it?
1. In general, what is the proper way to allow conversion of SomeType to SomeType except explicitly defining conversion operator? (Not in my example only).
2. If defining conversion operator is necessary, is reinterpret_cast is the proper way to do it? Or there are "cleaner" ways?
EDIT: Answers and comments were very helpful. I decided to provide more context right now. My problem is not with implementing const_iterator itself (I think that I know how to do it), but how to use same template for iterator and const_iterator. Here is what I mean
template <typename T>
struct iterator
{
iterator(Node<T>* _node) : node{ _node } {}
T& operator*() { return node->value; } // for iterator only
const T& operator*() const { return node->value; } // we need both for iterator
// for const iterator to be usable
iterator& operator++() { node = node->next; return *this; }
iterator operator++(int) { auto result = iterator{ node }; node = node->next; return result; }
bool operator==(const iterator& other) { return node == other.node; }
bool operator!=(const iterator& other) { return Node != other.node; }
private:
Node<T>* node;
};
Implementing const_iterator is essentially the same, except that T& operator*() { return node->value; }.
The initial solution is just to write two wrapper classes, one with T& operator*() and the other one without. Or use inheritance, with iterator deriving from const_iterator (which might be a good solution and has an advantage - we don't need to rewrite comparison operators for iterator and can compare iterator with const_iterator - which most often makes sense - as we check that they both point at same node).
However, I am curious how to write this without inheritance or typing same code twice. Basically, I think that some conditional template generation is needed - to have the method T& operator*() { return node->value; } generated only for iterator and not const_iterator. What is the proper way to do it? If const_iterator treated the Node* as Node*, it almost solves my problem.
Is there a better, "proper" way to do it?
There must be since your solution both has a weird behavior and is also invalid as specified by the C++ standard.
There's a rule called strict aliasing which dictate what kind of pointer type can alias another type. For example, both char* and std::byte* can alias any type, so this code is valid:
struct A {
// ... whatever
};
int main() {
A a{};
std::string b;
char* aptr = static_cast<void*>(&a); // roughtly equivalent to reinterpret
std::byte* bptr = reintepret_cast<std::byte*>(&b); // static cast to void works too
}
But, you cannot make any type alias another:
double a;
int* b = reinterpret_cast<int*>(&a); // NOT ALLOWED, undefined behavior
In the C++ type system, each instantiation of a template type are different, unrelated types. So in your example, Node<int> is a completely, unrelated, different type than Node<int const>.
I also said that your code has a very strange behavior?
Consider this code:
struct A {
int n;
A(int _n) : n(_n) { std::cout << "construct " << n << std::endl; }
A(A const&) { std::cout << "copy " << n << std::endl; }
~A() { std::cout << "destruct " << n << std::endl; }
};
Node<A> node1{A{1}};
Node<A> node2{A{2}};
Node<A> node3{A{3}};
node1.next = &node2;
node2.next = &node3;
Node<A const> node_const = node1;
This will output the following:
construct 1
construct 2
construct 3
copy 1
destruct 1
destruct 3
destruct 2
destruct 1
As you can see, you copy only one data, but not the rest of the nodes.
What can you do?
In the comments you mentionned that you wanted to implement a const iterator. That can be done without changing your data structures:
// inside list's scope
struct list_const_iterator {
auto operator*() -> T const& {
return node->value;
}
auto operator++() -> node_const_iterator& {
node = node->next;
return *this;
}
private:
Node const* node;
};
Since you contain a pointer to constant node, you cannot mutate the value inside of the node. The expression node->value yield a T const&.
Since the nodes are there only to implement List, I will assume they are abstracted away completely and never exposed to the users of the list.
If so, then you never have to convert a node, and operate on pointer to constant inside the implementation of the list and its iterators.
To reuse the same iterator, I would do something like this:
template<typename T>
struct iterator_base {
using reference = T&;
using node_pointer = Node<T>*;
};
template<typename T>
struct const_iterator_base {
using reference = T const&;
using node_pointer = Node<T> const*;
};
template<typename T, bool is_const>
using select_iterator_base = std::conditional_t<is_const, const_iterator_base<T>, iterator_base<T>>;
Then simply make your iterator type parameterized by the boolean:
template<bool is_const>
struct list_basic_iterator : select_iterator_base<is_const> {
auto operator*() -> typename select_iterator_base<is_const>::reference {
return node->value;
}
auto operator++() -> list_basic_iterator& {
node = node->next;
return *this;
}
private:
typename select_iterator_base<is_const>::node_ptr node;
};
using iterator = list_basic_iterator<false>;
using const_iterator = list_basic_iterator<true>;
Maybe you want another class altogether, like this:
template<typename T>
struct NodeView
{
T const& value; // Reference or not (if you can make a copy)
Node<T>* next;
NodeView(Node<T> const& node) :
value(node.value), next(node.next) {
}
};
Demo
If however you are talking about an iterator or a fancy pointer (as you mention in the comments), it's quite easy to do with an additional template parameter and some std::conditional:
template<typename T, bool C = false>
class Iterator {
public:
using Pointer = std::conditional_t<C, T const*, T*>;
using Reference = std::conditional_t<C, T const&, T&>;
Iterator(Pointer element) :
element(element) {
}
Iterator(Iterator<T, false> const& other) :
element(other.element) {
}
auto operator*() -> Reference {
return *element;
}
private:
Pointer element;
friend Iterator<T, !C>;
};
Demo
Related
I am working with legacy C code and the new code is written in C++. To use the C++ standard library, I wrote a simple Iterator for the legacy LinkedList as shown below after reading through Bjarne Stroustrup's blog post on Adaptation.
My question is:
I want to create another Iterator for another struct say struct TokenList. I am not sure how to use namespace and still be able to use the range-based for loops. Any pointers would be helpful.
Are the adapters for the Iterator namely: begin, end, ++, *, != correct? Currently, I'm an interested in reading the contents of the LinkedList using range-based for loops.
Coliru
#include <cstdio>
#include <numeric>
#include <algorithm>
struct LinkedList {
double v;
LinkedList *next;
};
struct Iterator {
LinkedList *current;
LinkedList &c;
};
Iterator begin(LinkedList *c) { return Iterator {c, *c}; }
Iterator end(LinkedList *c) { return Iterator {nullptr, *c}; }
Iterator &operator++(Iterator &p) { p.current = p.current->next; return p; }
LinkedList *operator*(Iterator p) { return p.current; }
bool operator!=(Iterator lhs, Iterator rhs) { return (lhs.current != rhs.current); }
int main()
{
LinkedList *node1 = new LinkedList;
LinkedList *node2 = new LinkedList;
LinkedList *node3 = new LinkedList;
node1->v = 1; node1->next = node2;
node2->v = 2; node2->next = node3;
node3->v = 3; node3->next = nullptr;
printf("// C style: iteration\n");
for (auto ptr = node1; ptr; ptr = ptr->next) {
printf("%e\n", ptr->v);
}
auto head = node1;
// make use of begin(), end(), ++, != and *
printf("// Modern C++ style: range based for-loop\n");
for (const auto& it : head) {
printf("%e\n", it->v);
}
delete node3;
delete node2;
delete node1;
return 0;
}
Iterators are pseudo-pointer types. That means they themselves are regular.
struct Iterator {
LinkedList *current;
LinkedList &c;
};
Here you mix references and pointers. This is a serious anti-pattern, as what does assignment do? There is no sensible answer.
I would remove the c member entirely.
Next you need to broadcast an iterator type. Yours looks like a forward iterator. All end iterators can be equal.
Iterator begin(LinkedList *c) { return Iterator {c, *c}; }
Iterator end(LinkedList *c) { return Iterator {nullptr, *c}; }
These look ok. Just remove *c.
Note that the name does not have to be Iterator. begin/end must be defined in the namespace of LinkedList, but the return type does not have to be.
Iterator &operator++(Iterator &p) { p.current = p.current->next; return p; }
I usually implement this as a member function, and implement both pre and post increment; post is implemented using pre and copy.
LinkedList *operator*(Iterator p) { return p.current; }
This is wrong. It should return *p.current as a double&.
bool operator!=(Iterator lhs, Iterator rhs) { return (lhs.current != rhs.current); }
sure. Also implement == as !(lhs!=rhs).
Look up the forward iterator concept and forward iterator tag. Include the types needed for std::iterator_traits.
For other things to iterate, give the iterator a different name. This can be via a different namespace.
If the thing that differs is just the type of the value, you can make it a template pretty easy. Then you only have to manually write begin/end.
If the name of v also changes, you could use ADL on a GetValue(List*) function you write as a customization point.
Now, being usable in a ranged based for is different than being an iterator. Ranged based for is a tad easier; but the above upgrades you to a full forward iterator, which in turn reduces surprise when you try to use a std algorithm or basically anything else.
How I would write it:
// Iteration::LinkedListIterator<X> assumes that X is a linked list node
// with members ->next and ->value. If it isn't, override the customization
// points GetNextNode and GetListElement in the namespace of X.
namespace Iteration {
template<class List>
List* GetNextNode( List* l ) {
if (!l) return l;
return l->next;
}
template<class List>
decltype(auto) GetListElement( List* l ) {
return l->value;
}
template<class List>
struct LinkedListIterator {
using self=LinkedListIterator;
List *current;
self& operator++(){ current = GetNextNode(current); return *this; }
self operator++(int)&{ auto copy = *this; ++*this; return copy; }
decltype(auto) operator*() {
return GetListElement(current);
}
decltype(auto) operator*() const {
return GetListElement(current);
}
auto operator->() {
return std::addressof(GetListElement(current));
}
auto operator->() const {
return std::addressof(GetListElement(current));
}
friend bool operator==(self const& lhs, self const& rhs) {
return lhs.current == rhs.current;
}
friend bool operator!=(self const& lhs, self const& rhs) {
return lhs.current != rhs.current;
}
using iterator_category = std::forward_iterator_tag;
using value_type = std::decay_t<decltype(GetListElement(std::declval<List*>()))>;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
};
};
struct LinkedList {
double v;
LinkedList *next;
};
// customization point; the name of
double& GetListElement( LinkedList* l ) { return l->v; }
double const& GetListElement( LinkedList const* l ) { return l->v; }
Iteration::LinkedListIterator<LinkedList> begin( LinkedList* l ) {
return {l};
}
Iteration::LinkedListIterator<LinkedList> end( LinkedList* l ) {
return {nullptr};
}
I have a class containing such a structure:
class Store {
private:
struct pointNode {
T data;
std::unique_ptr<pointNode> next;
pointNode():data(std::move(T{})),next(std::move(std::unique_ptr<pointNode>{})){}
pointNode(T n_data, std::unique_ptr<pointNode> ptr) : data(std::move(n_data)), next(std::move(ptr)) {}
};
std::unique_ptr<pointNode> head;
public:
decltype(auto) Begin() ;
};
I need to create a Begin function that will get a pointer to head.
I wrote something like this:
template<typename T>
decltype(auto) Stack<T>::Begin()
{
std::shared_ptr<pointNode> sh_ptr=std::make_shared<pointNode>(head);
return std::move(sh_ptr);
}
That is, create a shared_ptr and return it from the function, then go through the list.
But:
std::shared_ptr<pointNode> sh_ptr=std::make_shared<pointNode>(head);
returns error C2664
Error C2664 "Stack<int>::pointNode::pointNode(Stack<int>::pointNode &&)":
argument 1 cannot be converted from "std::unique_ptr<Stack<int>::pointNode,std::default_delete<Stack<int>::pointNode>>"
in "const Stack<int>::pointNode &"
I tried to remove decltype (auto), but everything is the same .Maybe someone understands what I'm doing wrong .I would be happy to help, thank you
unique_ptr cannot share the data with shared_ptr. That's the point of a unique pointer. But if you want to transfer the ownership, you can do it like so:
#include <string>
#include <memory>
int main()
{
std::unique_ptr<std::string> up( new std::string("Hello, World!\n") );
// up owns the object
// Transfer ownership
std::shared_ptr<std::string> sp( up.release() );
// sp owns the object, up is empty.
}
Having said that, returning a shared_ptr from the begin() function is likely not what you want. I would guess that you want to return a reference or an iterator from that function. Here's how you can return a reference:
template<typename T>
pointNode& Stack<T>::begin()
{
return *head;
}
Note that, for this to work like STL iterators, you need to create a new iterator type. begin returning a refence to a private node class is not a great interface and leaks implementation details.
unique_ptrs claim unique ownership. The clean way to transfer ownership to a shared_ptr would be to move it. So if you have
std::unique_ptr<pointNode> head;
then
std::shared_ptr<pointNode> sh_ptr( std::move(head) );
This is however a design flaw.
You do not want to transfer ownership of the container's elements. Here you actually want to share the raw pointer with the iterator. Any iterator that you have should be able to use that raw pointer - but not destroy the element it points at.
The first draft of a simple forward list type of container would be something like the below - and nowhere should a unique_ptr or shared_ptr be used:
template<class T>
class Store {
private:
struct Node {
T data;
Node* next;
};
Node* head = nullptr;
public:
struct iterator {
iterator(Node* x = nullptr) : current(x) {}
T& operator*() { return current->data; }
T* operator->() { return ¤t->data; }
iterator& operator++() { current = current->next; return *this; }
bool operator!=(const iterator& rhs) const {
return current != rhs.current;
}
private:
Node* current;
};
~Store() {
Node* next;
while(head) {
next = head->next;
delete head;
head = next;
}
}
iterator begin() { return head; }
iterator end() { return nullptr; }
};
Demo
I'm having a problem getting my linked list (it's actually a square list) passing tests that have been given by my professor, and I'm not sure what I'm supposed to do.
Here's my code:
/** LinkedList class declaration. */
template <typename T>
class LinkedList;
template <class TNode>
class Iterator
{
/* Helper class to provide pointer like facilities around a node */
friend class LinkedList<typename TNode::value_type>;
TNode* pNode; //The node oriented with this instance of iterator.
//Iterator(TNode* _pNode) : pNode(_pNode) {}
public:
Iterator(TNode* _pNode) : pNode(_pNode) {}
using value_type = typename TNode::value_type;
//using size_type = std::size_type;
using pointer = TNode*;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using iterator = Iterator<TNode>;
using iterator_category = std::bidirectional_iterator_tag;
.............removed unneeded code...............
value_type get() {
return pNode->_data;
}
typename TNode::value_type &operator*(){ return pNode->_data; }
};
template <typename T>
class Node
{
friend class LinkedList<T>;
friend class Iterator<Node<T> >;
Node() : _next(0), _prev(0), _head(0), _nextHead(0), _prevHead(0) {}
Node(T data) : _data(data), _next(0), _head(0), _nextHead(0), _prevHead(0) {}
Node(T data, Node<T>* next, Node<T>* prev, Node<T>* head, Node<T> nextHead, Node<T> prevHead) :
_data(data), _next(next), _prev(prev), _head(head), _nextHead(nextHead), _prevHead(prevHead){}
T _data;
Node<T>* _next;
Node<T>* _prev;
Node<T>* _head;
Node<T>* _nextHead;
Node<T>* _prevHead;
public:
typedef T value_type;
};
template <typename T>
class LinkedList
{
public:
using size_type = std::size_t;
private:
Node<T>* first;
Node<T>* last;
Node<T>* lastHead;
size_type _count = 0;
double columnNumbers = 0;
public:
typedef T value_type;
using pointer = std::unique_ptr<Node<T>>;
using iterator = Iterator<Node<T>>;
using difference_type = std::ptrdiff_t;
using reference = T&;
using const_reference = T const&;
using const_pointer = T const*;
using const_iterator = iterator const;
using reverse_iterator = std::reverse_iterator < iterator >;
using const_reverse_iterator = reverse_iterator const;
LinkedList() : first(0), last(0), lastHead(0) { }
~LinkedList()
{
.............removed unneeded code...............
}
iterator begin(){ return iterator(first); }
iterator end(){ return iterator(last); }
const_iterator begin() const { return const_iterator(first); }
const_iterator end() const { return const_iterator(last); }
const_iterator cbegin() const { return const_iterator(first); }
const_iterator cend() const { return const_iterator(last); }
reverse_iterator rbegin() { return reverse_iterator(last); }
reverse_iterator rend() { return reverse_iterator(first); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(last); }
const_reverse_iterator rend() const { return const_reverse_iterator(first); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(last); }
const_reverse_iterator crend() const { return const_reverse_iterator(first); }
.............removed unneeded code...............
void insert(T data)
{
.............removed unneeded code...............
}
void reorder() { // this reorders the head pointers so they are all in the correct spot for the square list
.............removed unneeded code...............
}
bool erase(iterator& _iNode) //True for success, vice versa
{
.............removed unneeded code...............
}
void clear()
{
.............removed unneeded code...............
}
};
template <typename T>
bool operator==(Iterator<Node<T>> const& lhs, Iterator<Node<T>> const& rhs){
return lhs.compare(rhs);
}
Here's the test I am supposed to run
BOOST_AUTO_TEST_CASE(ut_Rvalue_insert_scrambled_int) {
typedef std::unique_ptr<int> UP;
std::vector<int> data{ 9, 10, 7, 8, 5, 6, 3, 4, 1, 2 };
LinkedList<UP> sqi;
for (auto datum : data) {
sqi.insert(UP(new int(datum)));
}
std::sort(data.begin(), data.end());
std::vector<int> dup;
for (auto iter = sqi.begin(); iter != sqi.end(); ++iter) {
dup.push_back(*iter->get());
}
std::sort(data.begin(), data.end());
std::sort(dup.begin(), dup.end());
BOOST_CHECK(dup.size() == data.size());
BOOST_CHECK_EQUAL_COLLECTIONS(dup.begin(), dup.end(), data.begin(), data.end());
}
When compiling, I get these errors:
Error 1 error C2819: type 'Iterator<Node<T>>' does not have an overloaded member 'operator ->' ut_square_list_10_insert_rvalue.cpp 33
and
Error 2 error C2232: '->Iterator<Node<T>>::get' : left operand has 'class' type, use '.' ut_square_list_10_insert_rvalue.cpp 33 1
So, I know this is an issue relating to pointers, but I don't know how, or what I should be doing here.
In particular, it's this line...
dup.push_back(*iter->get());
Is there a better way to set this up, or is he requiring me to overload the -> operator?
I tried changing it to this (even though my prof will not want it this way -- he rips the current ut files out and puts fresh copies in, so he wants it to work the above way, and not this way)
dup.push_back(*iter.get());
It no longer gives me the overloaded errors, but is it giving me this now:
Error 1 error C2280: 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function
Ok, lets look at the types here.
You have a LinkedList<UP> and std::vector<int>.
So when you're trying to push an element to vector using list's iterator, you have to get a UP value from iterator using iter.get(), and then dereference it using operator *.
So the final line should look like this:
dup.push_back(*iter.get());
Now that this project has been submitted and marked, I thought I would give the answer.
value_type *operator->() const {
TNode* node = pNode;
value_type* nodeData = &node->_data;
return nodeData;
}
Essentially, from what I understand, and the way I created my linked list, I needed to overload the -> operator and pass out a pointer to the data reference.
If someone could explain this a bit more I would really appreciate it. It's tough to wrap my head around why I would need to do this, but this is the only way I could figure out how to achieve this, and was the accepted answer by the prof (I didn't lose any marks on the project).
Im trying to write an efficient list and node classes that have minimal code to iterate over them. However I'm having difficulty fulfilling all my criteria. I want this code to be used safely in a multi-threaded environment and so if possible I want all the functions and arguments for iteration to be const to ensure no state is being written to ie its all read only. So I wrote the below classes and iteration code:
template<typename _Type_>
class Node
{
template<typename _Type_> friend class List;
public:
Node() : m_Next(NULL) {}
Node(const _Type_& value) : m_Value(value), m_Next(nullptr()) {}
const _Type_& Get() const { return m_Value; }
Node&& Next() const { return std::move(*m_Next); }
operator _Type_ () const { return m_Value; }
_Type_& operator->() { return m_Value; }
const _Type_& operator->() const { return m_Value; }
operator bool() const { return m_Next != nullptr(); }
private:
_Type_ m_Value;
Node* m_Next;
};
template<typename _Type_>
class List
{
public:
typedef Node<_Type_> Node;
List() : m_Head(NULL) {}
void AddHead( const _Type_& value )
{
Node* pNode = GE_NEW(Node, value);
pNode->m_Next = &m_Head;
m_Head = *pNode;
}
Node&& Head() const { return std::move(m_Head); }
private:
Node m_Head;
};
and the all important iteration code:
RenderTargetList::Node&& renderTarget = m_Targets.Head();
while( renderTarget )
{
if(renderTarget->Format() == format)
{
return renderTarget.Get();
}
renderTarget = renderTarget.Next();
}
But this doesn't compile as:
Node&& Head() const { return std::move(m_Head); }
returns a non const rvalue reference in a const function function ie it has to be:
const Node&& Head() const { return std::move(m_Head); }
instead, but then this doesn't work as the iteration code fails on the assignment:
renderTarget = renderTarget.Next();
because renderTarget must now be defined as:
const RenderTargetList::Node&& renderTarget = m_Targets.Head();
because head returns a const. Basically I seem to be in a right mess of const, references, lvalues and rvalues. Someone please help!
Thanks
Here's the basic version of a list class. Note that the iterator type and the node type are two distinct types. This way, the node type can own the value, and the iterator type can have pointer semantics.
I'll post this as a community wiki, as it's rather a comment than a direct answer to the question.
template<typename Type>
class List
{
private:
// note that for immutable nodes, we could store `Type const`
// and for an immutable list, `Node const* const`
struct Node
{
Type m_Value;
Node* m_pNext;
};
Node* m_pHead;
public:
class const_iterator
{
private:
Node const* m_pNode;
friend List;
const_iterator(Node* p_pNode) : m_pNode(p_pNode) {}
public:
const_iterator() : m_pNode(nullptr) {}
explicit operator bool() const
{ return m_pNode; }
const_iterator Next() const
{ return {m_pNode->m_pNext}; }
Type const& Get() const
{ return m_pNode->m_Value; }
friend bool operator!=(const_iterator const& lhs,
const_iterator const& rhs)
{
return lhs.m_pNode != rhs.m_pNode;
}
};
List() : m_pHead(nullptr) {}
~List()
{
// delete nodes
}
List(List const&) = delete; // needs to be customized
List& operator=(List const&) = delete; // this one, too
// the only function that modifies the list:
void AddHead( Type const& value )
{
Node* pNode = new Node{value, m_pHead};
m_pHead = pNode;
}
const_iterator Head() const
{ return {m_pHead}; }
};
Usage example:
#include <iostream>
int main()
{
List<int> l;
for(int i = 0; i < 10; ++i) l.AddHead(i);
auto it = l.Head();
while(it)
{
std::cout << it.Get() << ", ";
it = it.Next();
}
}
I think you are confused at to the point of const if your goal is thread safety -- const does not give you thread safe code, only considerate and well understood access patterns will give you thread safe code.
I have lost of code which is thread sfae which does not make use of const, and you will need to as well if you want to be able to keep the "AddHead" method which certainly in its current form is not thread safe.
If your code is threaded you will need to add mutex locks around the code where you are accessing shared data which could be modified or read by another thread -- the critical zones -- from your code structure these critical zones could be very short lived, so not much of a penalty.
Please tell me how the operator->() in being defined for the iterator of std::list in order to refer members of the element that is being pointed by an iterator.
EDIT:
The problem is that if you implement like this (Fred Nurk):
template<class T>
struct list {
private:
struct Node { // internal class that actually makes up the list structure
Node *prev, *next;
T data;
};
public:
struct iterator { // iterator interface to the above internal node type
T* operator->() const {
return &_node->data;
}
private:
Node *_node;
}
};
Then when you write:
struct A {
int n;
};
void f() {
list<A> L; // imagine this is filled with some data
list<A>::iterator x = L.begin();
x->n = 42;
}
Then
x->n I understand like x->operator->()n which is equivalent to (A ponter to a)n which is a nonsence. How to understand this part. Some answers tell that it is equivalent to x->operator->()->n; (instead of x->operator->()n) but I don't understand why. Please explain me this.
(hint: It returns a pointer to the element.)
The -> operator behaves as follows:
T->x; // some field
T->foo(); // some function
...is equivalent to:
T.operator->()->x;
T.operator->()->foo();
Note the re-application of -> to whatever is returned.
With many details elided, here is the gist of how it works:
template<class T>
struct list {
private:
struct Node { // internal class that actually makes up the list structure
Node *prev, *next;
T data;
};
public:
struct iterator { // iterator interface to the above internal node type
T* operator->() const {
return &_node->data;
}
private:
Node *_node;
}
};
Thus, given:
struct A {
int n;
};
void f() {
list<A> L; // imagine this is filled with some data
list<A>::iterator x = L.begin();
x->n = 42;
// x.operator->() returns an A*
// which gets -> applied again with "n"
}
Operator -> is implemented differently than the other operators in C++. The operator function is expected to return a pointer, and the -> is applied again to that pointer.
It returns pointer. Just for completeness, here is simple example:
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
class SafeVectorIterator
{
vector<T> & _vec;
size_t _pos;
public:
SafeVectorIterator(vector<T> & vec) : _vec(vec), _pos(0) { }
void operator++() { ++_pos; }
void operator--() { --_pos; }
T& operator*() { return _vec.at(_pos); }
T* operator->() { return &_vec.at(_pos); }
};
struct point { int x, y; };
int main()
{
vector<point> vec;
point p = { 1, 2 };
vec.push_back(p);
vec.push_back(p);
SafeVectorIterator<point> it(vec);
++it;
it->x = 8;
cout << (*it).y << '\n';
return 0;
}