-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhashmap_iterator.h
227 lines (196 loc) · 8.43 KB
/
hashmap_iterator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*
* Assignment 2: HashMapIterator template interface and implementation
* Created by Avery Wang (lecturer for 2019-2020 - [email protected])
*
* DO NOT EDIT THIS FILE
* For bug reports please email your current CS 106L lecturer.
*/
#ifndef HASHMAPITERATOR_H
#define HASHMAPITERATOR_H
#include <iterator> // for std::forward_iterator_tag
#include <functional> // for std::conditional_t
// forward declaration for the HashMap class
template <typename K, typename M, typename H> class HashMap;
/*
* Template class for a HashMapIterator
*
* Map = the type of HashMap this class is an iterator for.
* IsConst = whether this is a const_iterator class.
*
* Concept requirements:
* - Map must be a valid class HashMap<K, M, H>
*/
template <typename Map, bool IsConst = true>
class HashMapIterator {
public:
/*
* This alias is very important. When dealing with const_iterator, the value_type is always const, and
* that prevents the client from modifying the elements via a const_iterator. The meta-function
* std::conditional_t changes the value_type (at compile-time) to a const one if IsConst is true.
*/
using value_type = std::conditional_t<IsConst, const typename Map::value_type, typename Map::value_type>;
/*
* Public aliases for this iterator class. Important so STL functions like std::iterator_traits
* can parse this class for important information, like its iterator category.
*/
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
/*
* Friend declarations so the HashMap class this iterator is for can access the attributes of its iterators.
* Also, to make conversions between iterator and const_iterator easy, we declare the corresponding
* iterator and const_iterators as friends.
*/
friend Map;
friend HashMapIterator<Map, true>;
friend HashMapIterator<Map, false>;
/*
* Conversion operator: converts any iterator (iterator or const_iterator) to a const_iterator.
*
* Usage:
* iterator iter = map.begin();
* const_iterator c_iter = iter; // implicit conversion
*
* Implicit conversion operators are usually frowned upon, because they can cause
* some unexpected behavior. This is a rare case where a conversion operator is
* very convenient. Many of the iterator functions in the HashMap class are
* secretly using this conversion.
*
* Note: conversion in the opposite direction (const to non-const) is not safe
* because that gives the client write access the map itself is const.
*/
operator HashMapIterator<Map, true>() const {
return HashMapIterator<Map, true>(_buckets_array, _node, _bucket);
}
/*
* Dereference operators: defines the behavior of dereferencing an iterator.
*
* Usage:
* auto [key, value] = *iter;
* auto value = iter->second;
* iter->second = 3; // if iter is a regular iterator (not a const_iterator)
*
* Note that dereferencing an invalid or end() iterator is undefined behavior.
*/
reference operator*() const;
pointer operator->() const;
/*
* Increment operators: moves the iterator to point to the next element, or end().
*
* Usage:
* ++iter; // prefix
* iter++; // postfix
*
* Note: the prefix operator first increments, and the returns a reference to itself (which is incremented).
* The postfix operator returns a copy of the original iterator, while the iterator itself is incremented.
*
* Note that incrementing an invalid or end() iterator is undefined behavior.
*/
HashMapIterator<Map, IsConst>& operator++();
HashMapIterator<Map, IsConst> operator++(int);
/*
* Equality operator: decides if two iterators are pointing to the same element.
*
* Usage:
* if (iter == map.end()) {...};
*/
template <typename Map_, bool IsConst_>
friend bool operator==(const HashMapIterator<Map_, IsConst_>& lhs, const HashMapIterator<Map_, IsConst_>& rhs);
/*
* Inequality operator: decides if two iterators are pointing to different elements.
*
* Usage:
* if (iter != map.end()) {...};
*/
template <typename Map_, bool IsConst_>
friend bool operator!=(const HashMapIterator<Map_, IsConst_>& lhs, const HashMapIterator<Map_, IsConst_>& rhs);
/*
* Special member functions: we explicitly state that we want the default compiler-generated functions.
* Here we are following the rule of zero. You should think about why that is correct.
*/
HashMapIterator(const HashMapIterator<Map, IsConst>& rhs) = default;
HashMapIterator<Map, IsConst>& operator=(const HashMapIterator<Map, IsConst>& rhs) = default;
HashMapIterator(HashMapIterator<Map, IsConst>&& rhs) = default;
HashMapIterator<Map, IsConst>& operator=(HashMapIterator<Map, IsConst>&& rhs) = default;
private:
/*
* Determines what is the type of the nodes that the HashMap is using.
*/
using node = typename Map::node;
/*
* Determines what is the type of the _buckets_array that the HashMap is using.
*/
using bucket_array_type = typename Map::bucket_array_type;
/*
* Instance variable: a pointer to the _buckets_array of the HashMap this iterator is for.
*/
bucket_array_type* _buckets_array;
/*
* Instance variable: pointer to the node that stores the element this iterator is currently pointing to.
*/
node* _node;
/*
* Instance variable: the index of the bucket that _node is in.
*/
size_t _bucket;
/*
* Private constructor for a HashMapIterator.
* Friend classes can access the private members of class it is friends with,
* so HashMap is able to call HashMapIterator's private constructor
* (e.g, in begin()). We want the HashMapIterator constructor to be private
* so a client can't randomly construct a HashMapIterator without asking for one
* through the HashMap's interface.
*/
HashMapIterator(bucket_array_type* buckets_array, node* node, size_t bucket);
};
template <typename Map, bool IsConst>
HashMapIterator<Map, IsConst>::HashMapIterator(bucket_array_type* buckets_array, node* node,
size_t bucket) :
_buckets_array(buckets_array),
_node(node),
_bucket(bucket) { }
template <typename Map, bool IsConst>
typename HashMapIterator<Map, IsConst>::reference HashMapIterator<Map, IsConst>::operator*() const {
return _node->value; // _node can't be nullptr - that would be dereferencing end()
}
/*
* operator-> is a bit of an odd-ball. When you write p->m, it is interpreted as (p.operator->())->m.
* This means operator-> should return a pointer to the object that has the field m.
*
* For our usage, the client will write something like: iter->first, which is interpreted
* as (iter.operator->())->first. This means operator-> should return a pointer to the
* object that has the 'first' and 'second' field, i.e. a pointer to the std::pair/the value of that node.
*/
template <typename Map, bool IsConst>
typename HashMapIterator<Map, IsConst>::pointer HashMapIterator<Map, IsConst>::operator->() const {
return &(_node->value); // _node can't be nullptr - that would be dereferencing end()
}
template <typename Map, bool IsConst>
HashMapIterator<Map, IsConst>& HashMapIterator<Map, IsConst>::operator++() {
_node = _node->next; // _node can't be nullptr - that would be incrementing end()
if (_node == nullptr) { // if you reach the end of the bucket, find the next bucket
for (++_bucket; _bucket < _buckets_array->size(); ++_bucket) {
_node = (*_buckets_array)[_bucket];
if (_node != nullptr) {
return *this;
}
}
}
return *this;
}
template <typename Map, bool IsConst>
HashMapIterator<Map, IsConst> HashMapIterator<Map, IsConst>::operator++(int) {
auto copy = *this; // calls the copy constructor to create copy
++(*this);
return copy;
}
template <typename Map, bool IsConst>
bool operator==(const HashMapIterator<Map, IsConst>& lhs, const HashMapIterator<Map, IsConst>& rhs) {
return lhs._node == rhs._node;
}
template <typename Map, bool IsConst>
bool operator!=(const HashMapIterator<Map, IsConst>& lhs, const HashMapIterator<Map, IsConst>& rhs) {
return !(lhs == rhs);
}
#endif // HASHMAPITERATOR_H