Overload operator-> for STL iterator - c++

I'm writing my own implementation of the C++ STL map container. Now I'm trying to implement the iterator. It should allow you to do something like iter->first and iter->second that returns the key/value respectively and where iter is an object not a pointer. I'm wondering how I should overload this? It's a bit confusing because I'm not sure what the return type should be; it has to be an object with members first/second I suppose. Is it typical to return a reference to a wrapper/interface object or something like that?

If you really mean the C++ standard library, then the value_type of a map is a pair. A pair has members first and second. Dereferencing an iterator in a map gives you a pair.

Yes, you'll need a proxy to hold the relevant reference.
As for the type: standard-library iterators typically dereference to something of type value_type. For map<K,V>, the value type is std::pair<K, V> (or rather, pair<key_type, mapped_type>), which is where you get the first/second interface from.
(One of Stephan Lavavej's lectures explains how the MSVC++ implementation uses the same underlying data structure for set and map; the only difference is that set::value_type equals set::key_type, while map::value_type is pair<key_type, mapped_type>. That way you can tell the two apart with a simple trait check, but the iterator interface is virtually identical.)

The value_type of a standard map is std::pair<const KeyType, MappedType>.
To achieve normal pointer semantics, operator* returns a reference, whereas operator-> returns a pointer.
//minimal example
#include <utility>
#include <cstdio>
struct It
{
std::pair<const int, int> pair;
std::pair<const int, int>* operator->() { return &pair; }
std::pair<const int, int>& operator*() { return pair; }
};
int main()
{
It it = {std::make_pair(10, 20) };
(*it).second = 30;
std::printf("%d %d\n", it->first, it->second);
}

std::map<K,V>::iterator iterates over objects of type std::pair<K,V>.

The answer to your question is yes. You should return a proxy-object or a reference to a proxy-object to get that behavior.

Related

Correctly return pointer to key/value pair from bespoke associative container & iterator

I have an associative container that is implemented internally as two linear arrays, one of keys, and one of values. Both arrays are kept sorted so a binary search of keys yields the index of the value. This is done to improve the performance of searches when the size relationship between keys and values is grossly asymmetric. The container use case is modified rarely but searched often, which is why we have designed it this way.
template<typename Key, typename Value>
struct Container
{
// allocated on create
Key* mKeys;
Value* mValues;
unsigned mCount;
class iterator
{
Container* mContainer;
unsigned mIndex;
// return a pair that can support mutating the value but not the key
std::pair<const Key &,Value &> MakePair() {
return std::make_pair( mContainer->mKeys[mIndex],
mContainer->mValues[mIndex] );
};
// I don't have a std::pair to return here; and I can't return a temporary.
std::pair<const Key,Value> & operator*();
std::pair<const Key,Value> * operator->();
}
}
I am trying to implement an iterator over the container that returns a key/value pair similar to std::map.
My concern is around reference operator*() and pointer operator->(), where I don't actually have a std::pair to return a reference to. However, I would still like to use the syntax of container.begin()->first, or (*container.begin()).second.
Does anyone have any insights on how I can return a reference or pointer to a std::pair<const key, value> here? Or, can I simply return something by value which holds references to the key and value, named first and second, respectively, to get compliance?

Change/update value using std::map const_iterator

I am just curious to know if I can change/update the map's value using const_iterator.
Below is the code snippet:
int main()
{
map <int, int> m;
m.insert(make_pair(1, 10));
map <int, int>::const_iterator itr = m.begin(); //The iterator is const_iterator
itr->second = 30;
cout << itr->second; //The value to be printed is 30, and not 10.
return 0;
}
Thank you in advance for sharing your ideas.
The whole point of const_iterator is that it cannot be used to modify the container. So no.
It is not possible to change an element through a const iterator. That's the most important distinction between a const iterator and non-const iterator.
std::map::begin returns a non-const iterator (assuming the object operand is non-const), so there is no need to use a const iterator in the first place.
However, if for some reason (not demonstrated in the example) you can only have a const iterator, but have non-const access to the container, then you can get a non-const iterator to the element pointed by the const iterator. This can be achieved by using following, which looks like a trick:
map <int, int>::iterator mutable_itr = m.erase(itr, itr);
It doesn't erase anything, because [itr, itr) is an empty range.
Can I change/update the map's value using const_iterator?
No!
It's called const iterator for a reason. As mentioned here, A const_iterator is an iterator that points to const value (like a const T* pointer); dereferencing it returns a reference to a constant value (const T&) and prevents modification of the referenced value: it enforces const-correctness.
The compiler should not allow modifying the contents of a map through a const_iterator. The line
itr->second = 30;
should be reported as an error. If your compiler allows that line, then it is not standards compliant. Perhaphs it allows that line to be compiled through flags that allow standards-incompatible behavior.
Using g++, I get the following error.
socc.cc: In function ‘int main()’:
socc.cc:12:19: error: assignment of member ‘std::pair<const int, int>::second’ in read-only object
itr->second = 30;
const iterator is supposed to prevent the user from changing/modiyfing the value by any means.

How to make idempotent taking a reference to a dereference of an iterator

The code bellow (-std=c++11) according to a "naive" view should work.
Instead it doesn't (should be known and understood why it doesn't).
Which is the shortest way of modifying the code (overloading &) in order to make it behave according to the "naive" view ?
Shouldn't that be given as an option during stl object creation (without writting too much) ?
#include <iostream>
#include <vector>
int main(int argc, char **argv)
{ std::vector<int> A{10,20,30};
auto i=A.begin();
auto j=&*i;
std::cout<<"i==j gives "<<(i==j)<<std::endl;
return 0;
}
The problem cannot be solved. There are three reasons it cannot be solved.
First problem
The operator & you need to overload is the operator & for the element type of the vector. You cannot overload operator & for arbitrary types, and in particular you can't overload it for built-in types (like int in your example).
Second problem
Presumably you want this to work for std::vector, std::array, and built-in arrays? Also probably std::list, std::deque, etc? You can't. The iterators for each of those contains will be different (in practise: in theory, some of them could share iterators, but I am not aware of any standard library where they do.)
Third problem
If you were prepared to accept that this would only work for std::vector<MyType>, then you could overload MyType::operator & - but you still couldn't work out which std::vector<MyType> the MyType object lives in (and you need that to obtain the iterator).
First of, in your code snippet i deducts to std::vector<int>::iterator and j deducts to int*. The compiler doesn't know how to compare std::vector<int>::iterator against int*.
For this to work out, you could provide an overloaded operator== that would compare vector iterators against vector value type pointers in the following manner:
template<typename T>
bool operator==(typename std::vector<T>::iterator it, T *i) {
return &(*it) == i;
}
template<typename T>
bool operator==(T *i, typename std::vector<T>::iterator it) {
return it == i;
}
Live Demo
This shouldn't work - not even "accoding to a 'naive"' view". Eventhough every pointer is an iterator the reverse is not necessarily true. Why would you expect that to work?
It would work under two scenarios:
The iterator of the std::vector<T> implementation is actually a T*. Then your code would work since decltype(i) == int* and decltype(j) == int*). This MAY be the case for some compilers but you shouldn't even rely on it if it was true for your compiler.
The dereference operator does not return an object of type T but rather something that is convertible to T and has an overloaded operator& which gives the iterator back. This is not the case for very good reasons.
You could -as other have suggested- overload operator== to check whether both indirections (pointer and iterator) reference the same object but I suspect that you want the address of operator to give you back the iterator which cannot be accomplished if the iterator is not a pointer because the object type which is stored in the vector has no notion of vector/iterator or whatever.
The problem isn't in the equality operator, what I need is to define the dereference operator to give an iterator
You can't. The dereference operator in question is std::vector<int>::iterator which is part of the standard library and you can (and should not) manipulate it.
Note that since C++11 in a std::vector<T, A>,
value_type is T and
reference is T&.
Furthermore, the following is true:
All input iterators i support *i which gives a value of type T which is the value type of that iterator.
The iterator of std::vector<T> is required to have T as its value type.
An iterator of std::vector<T> is an input iterator.

Casting from pair<K,V>& to pair<const K,V>& [duplicate]

So I have a smart iterator that emulates a map const_iterator, and it needs to build the return type internally. Obviously, I'd like to store a pair<Key, Value> in my iterator class (since I need to modify it), but at the same time I'd like the dereference functions to present a pair<const Key, Value> (actually it would be a const pair<const Key, Value>& and const pair<const Key, Value>* respectively). The only solution I've come up with so far is to dynamically allocate a new pair every time change the value that my iterator class points to changes. Needless to say, this is not a good solution.
I've also tried *const_cast<const pair<const Key, Value> >(&value) where value is declared as pair<Key, Value>.
Any help would be greatly appreciated (as would the knowledge that it can't be done).
EDIT
For the curious: I ended up storing a pair<const Key, Value> p in my iterator class. In order to change the pair I alter the two elements separately based on the underlying iterator (map<Key, Value>::const_iterator it), const_casting the key so that it could be altered, like this:
*const_cast<Key*>(&p.first) = it->first;
p.second = it->second;
Not a solution I'm terribly happy with, but it gets the job done, and the dereference methods are happy because I'm storing something of the correct type, which they can refer to.
You can convert a value of type pair<Key,Value> to pair<const Key,Value>.
However, reading the question carefully, you're actually asking if, given a pair<Key,Value> you can create a pointer or reference to pair<const Key,Value> referring to the same object.
The answer is no - the only situation where a reference or pointer to one type can refer to an object of a different type is if the object type inherits from the referenced type.
One possibility is to return a pair of references, pair<const Key&, Value&>, created from the pair you wish to reference.
Yes.
std::pair<int, double> p(1,2);
std::pair<const int, double> q = p; // no problem
//q.first = 8; // error
q.second = 9;
int b; double d;
std::pair<int &, double &> s(b,d);
std::pair<int const &, double &> t = s; // also fine
As Kerrek SB pointed out, you can construct std::pair<const Key, Value> from std::pair<Key, Value>. However, your original question implies that you want to avoid constructing std::pair objects each time your iterator is dereferenced.
Unfortunately, there is not a good way to do this. You may have to construct the pair object and actually store it somewhere, particular for operator->. Otherwise you have to be able to have your map actually store pair<const Key, Value> to be able to return references/pointers to it from your iterator. Basically to return a reference/pointer, it has to be stored somewhere in that form: it cannot be a temporary.
Avoid the const_cast. That's just asking for undefined behavior when you use it to cast pair this way even though it may work quite often.
I met exactly the same problem. My solution was to create a new map object as part of the iterator class, then add to it members that are missed in the upstream map class and return the reference to members of it. Not very efficient, but works.
Your solution have two issues:
Assigning a const variable with const_cast is undefined behavior. The compiler optimisations could provide strange results.
Any new dereference would invalidate the results of previous dereference. It should not to. So, depending of the usage of your iterator it may also produce strange results.
Corrected by #antonpp, this is an undefined behaviour that shouldn't be recommended any more.
This can be solved by reinterpret_cast, and it is purely a compile-time directive.
std::pair<int, double> p1 = { 1, 2.3 };
auto& p2 = reinterpret_cast<std::pair<const int, double>&>(p1); // type of p2 is pair<const int, double>&
//++(p2.first); // error: expression must be a modifiable lvalue
++(p1.first);
assert(p2.first == 2);
However, reinterpret_cast is a dangerous behaviour. I would suggest adding an static assert, to prevent unexpected partial template specialization.
using srcType = std::pair<Key, Value>;
using tarType = std::pair<const Key, Value>;
static_assert(
offsetof(srcType, first) == offsetof(tarType, first)
&& offsetof(srcType, second) == offsetof(tarType, second)
);

Is it possible to cast a pair<Key, Value> to a pair<const Key, Value>?

So I have a smart iterator that emulates a map const_iterator, and it needs to build the return type internally. Obviously, I'd like to store a pair<Key, Value> in my iterator class (since I need to modify it), but at the same time I'd like the dereference functions to present a pair<const Key, Value> (actually it would be a const pair<const Key, Value>& and const pair<const Key, Value>* respectively). The only solution I've come up with so far is to dynamically allocate a new pair every time change the value that my iterator class points to changes. Needless to say, this is not a good solution.
I've also tried *const_cast<const pair<const Key, Value> >(&value) where value is declared as pair<Key, Value>.
Any help would be greatly appreciated (as would the knowledge that it can't be done).
EDIT
For the curious: I ended up storing a pair<const Key, Value> p in my iterator class. In order to change the pair I alter the two elements separately based on the underlying iterator (map<Key, Value>::const_iterator it), const_casting the key so that it could be altered, like this:
*const_cast<Key*>(&p.first) = it->first;
p.second = it->second;
Not a solution I'm terribly happy with, but it gets the job done, and the dereference methods are happy because I'm storing something of the correct type, which they can refer to.
You can convert a value of type pair<Key,Value> to pair<const Key,Value>.
However, reading the question carefully, you're actually asking if, given a pair<Key,Value> you can create a pointer or reference to pair<const Key,Value> referring to the same object.
The answer is no - the only situation where a reference or pointer to one type can refer to an object of a different type is if the object type inherits from the referenced type.
One possibility is to return a pair of references, pair<const Key&, Value&>, created from the pair you wish to reference.
Yes.
std::pair<int, double> p(1,2);
std::pair<const int, double> q = p; // no problem
//q.first = 8; // error
q.second = 9;
int b; double d;
std::pair<int &, double &> s(b,d);
std::pair<int const &, double &> t = s; // also fine
As Kerrek SB pointed out, you can construct std::pair<const Key, Value> from std::pair<Key, Value>. However, your original question implies that you want to avoid constructing std::pair objects each time your iterator is dereferenced.
Unfortunately, there is not a good way to do this. You may have to construct the pair object and actually store it somewhere, particular for operator->. Otherwise you have to be able to have your map actually store pair<const Key, Value> to be able to return references/pointers to it from your iterator. Basically to return a reference/pointer, it has to be stored somewhere in that form: it cannot be a temporary.
Avoid the const_cast. That's just asking for undefined behavior when you use it to cast pair this way even though it may work quite often.
I met exactly the same problem. My solution was to create a new map object as part of the iterator class, then add to it members that are missed in the upstream map class and return the reference to members of it. Not very efficient, but works.
Your solution have two issues:
Assigning a const variable with const_cast is undefined behavior. The compiler optimisations could provide strange results.
Any new dereference would invalidate the results of previous dereference. It should not to. So, depending of the usage of your iterator it may also produce strange results.
Corrected by #antonpp, this is an undefined behaviour that shouldn't be recommended any more.
This can be solved by reinterpret_cast, and it is purely a compile-time directive.
std::pair<int, double> p1 = { 1, 2.3 };
auto& p2 = reinterpret_cast<std::pair<const int, double>&>(p1); // type of p2 is pair<const int, double>&
//++(p2.first); // error: expression must be a modifiable lvalue
++(p1.first);
assert(p2.first == 2);
However, reinterpret_cast is a dangerous behaviour. I would suggest adding an static assert, to prevent unexpected partial template specialization.
using srcType = std::pair<Key, Value>;
using tarType = std::pair<const Key, Value>;
static_assert(
offsetof(srcType, first) == offsetof(tarType, first)
&& offsetof(srcType, second) == offsetof(tarType, second)
);