Reputation: 1014
I have stumbled over this article and wanted to try something similar.
but I'm stuck on how to assign the pointer to itself, well to the value map[10].value
after it has been created. the code from the article works, but sofar i haven't been beabled to replicate it.
the function however does work partially, a values gets inserted and the loop displays the added elements.
returning: 10 0
from the article:
template <typename T> struct key { mutable const T* p; key (const T* v = 0): p (v) {} bool operator< (const key& x) const {return *p < *x.p;} };
The key’s only purpose is to give us a mutable pointer that we can update. While at it, it also conveniently provides operator<. Ok, let’s look at our next draft (we will attend to that ??? in a second):
warning: warning: address of 'r.first' will always evaluate to 'true'.
#include <iostream>
#include <map>
using namespace std;
template <typename T>
struct key
{
mutable const T* p;
key (const T* v = 0): p (v) {}
bool operator< (const key& x) const {return *p < *x.p;}
};
struct CustomMap
{
typedef std::map<int, key<std::string>> key_map;
void insert (const int v)
{
auto r (key_map_.insert ( key_map::value_type (v, NULL)));
// if (r.second)
// r.first->second = &r.first;
// //r.first->first.p = &r.first->second.email;
if(r.second){
r.second = &r.first; // i wanna store the pointer, from the "Key" of a map in the value of the map element.
// warning: address of 'r.first' will always evaluate to 'true'
}
// i also tried this
if(r.first){
r.second = &r.first;
}
// and this
if(r.first->first == 0){
r.second = &r.first;
}
//return std::make_pair (r.first, r.second);
for(auto i : key_map_){
std::cout << i.first << " " << i .second.p << "\n";
}
std::cout << endl;
}
private:
key_map key_map_;
};
int main(){
CustomMap cm;
cm.insert(10);
return 0;
}
it can be done, but ...
Upvotes: 0
Views: 125
Reputation: 17007
It looks like you are stumbling over how to read the value returned by std::map::insert()
.
how to assign the pointer to itself, well to the value
map[10].value
after it has been created.
This much is good, even though it does lack some precision about what is assigned. You want to assign a value to map[10].value
. That is, assign something to the value part of the element of map
corresponding to the key (10
) that was just inserted. Let's look at the code you wrote.
auto r (key_map_.insert ( key_map::value_type (v, NULL)));
The keyword auto
is convenient, but it also hides the type being used. Do you know what the type of r
is? It's a pair. The second value, r.second
, indicates whether or not the insertion succeeded. (Insertion fails if the key already exists in the map.) The first value, r.first
is an iterator into the map. If the insertion succeeded (that is, if r.second
is true
), it points to the element that was just inserted; otherwise it points to the element that prevented the insertion. In either case, the key of the pointed-to element is the key you attempted to insert. That is, r.first->first == v
.
Using a bunch of std::pair
types does allow for code re-use within the standard library, but it does not make for readable code. The subexpression r.first->first
would be more understandable if it could be written (do not use the following) r.iterator->key
, but it cannot. Instead, you have to remember that r.first
is an iterator pointing to an element of your map, and the final first
selects the key from that element. The value of that map element would be the second part of the map's element, as in r.first->second
.
Moving on:
if(r.second){
This says "if the insertion succeeded".
r.second = &r.first; // i wanna store the pointer, from the "Key" of a map in the value of the map element.
Well, this line does not match the comment. The intent is to store something in the value of the map element, but it is actually being stored in the boolean indicating whether or not the insertion succeeded. The compiler rightfully issues a warning that converting the address of an object to a boolean will always result in true
. (No object's address is nullptr
.)
To match the comment, the left side of the assignment should be the value of the map element. Dereference the first
value in r
to get to the map element, then the second
member gives the value. And you probably want the p
member of that value, unless you've provided an operator for assigning a T*
to a key<T>
.
r.first->second.p = &r.first; // store something in the p field of the value of the map element.
// ^^^^^^ -- value of map element
// ^^^^^ -- iterator into the map
//^^ -- value returned by map::insert()
The right side of the assignment should be the value to be stored. It's doubtful you want to store the address of an iterator into your map. You probably want to store the address of the key of the element to which the iterator points.
r.first->second.p = &(r.first->first); // store a pointer to the key of a map element in the p field of the value of the same map element.
// ^^^^^^ -- key of map element
// ^^^^^ -- iterator into the map
// ^^ -- value returned by map::insert()
There is still a problem here because the types don't match up, but that's a separate issue. (The keys to your map are int
, but you chose to use key<std::string>
instead of key<int>
.)
Let's look at your other tries. (This is intended to help re-enforce your ability to read these expressions, not as criticism.)
if(r.first){
Testing the iterator returned by std::map::insert
is not helpful as that iterator always points to an element in the map. That's why the returned value has a boolean value as well as the iterator.
if(r.first->first == 0){
At least this is getting to the key of the inserted element, but you don't really want to limit yourself to acting on just the one element whose key is zero, do you? (You might have thought this was a test for nullptr
; it is not. The pointer in this setup would be r.first->second.p
.)
Upvotes: 1