tags:

views:

78

answers:

2

Essentially, what I'd like is for the value object to maintain a reference to the corresponding key object, because there's some useful information in there, which would be nice to access via the value object.

What I'm attempting to do may just not make sense, but consider the following:

class key
{
    // ... Various members ...
    friend bool operator< (const key &lhs, const key &rhs) { /* ... */ }
};

class value
{
public:
    value(const key &k) : k(k) {}
private:
     const key &k;
    // ... Various members ...

};


std::map<key,value> m;

// start a new scope, which may be due to e.g. function call, loop, etc.
{
    key k;  // I'm on the stack!

    m.insert(std::pair<key,value>(k, value(k)));
}

Of course, this doesn't work, because this is a reference to a stack object, which breaks as soon as k goes out of scope. Is there somehow a way to get a reference back to the copy of the key maintained in the map?

+1  A: 

Why not reference the member of value as your key?

class key { friend bool operator< (const key &,const key&); }
class value {
    public:
       value(const key &k) : k(k) {}
       const key &key() const {return k};
    private:
       key k;
}

std::map<key,value> m;
key k;
value v(k);
m.insert(std::pair<key,value>(v.key(),v));

... or somesuch. It seems like constructing the key inside the value object would generally be easier.

More like:

#include <map>
#include <iostream>

struct key {
    key(unsigned int ik) : k(ik) {}
    unsigned int k;
    friend bool operator< (const key &,const key &);
};
bool operator<  (const key &me,const key &other) {return me.k < other.k;}

struct value {
    value(unsigned int ik, unsigned int iv) : k(ik), v(iv) {}
    const key &theKey() const {return k;}
    unsigned int v;
    key k;
};

int main() {
    std::map<key,value> m;
    value v(1,3);
    m.insert(std::pair<key,value>(v.theKey(),v));

    for(std::map<key,value>::iterator it=m.begin(); it!=m.end();++it)
        std::cout << it->second.theKey().k << " " << it->second.v << "\n";
    return 0;
}
jkerian
I was trying to avoid making another copy of `k`, as it could potentially be quite heavyweight (which I know may be a sign of bad design). But apart from that, I think this would work. FYI: that last line may as well be `m.insert(std::pair<key,value>(k,v));`.
Oli Charlesworth
If possible, I would think it would be preferable to generate the key values out of value (probably in the constructor), my code above sortof got halfway there.
jkerian
@jkerian: I can't do what's in your second code snippet. In my real-world situation, I'm given a `key` object from elsewhere, and I need to then create (or locate) a `value` object in the map. In a nutshell: the `key`be created first.
Oli Charlesworth
Hmm... perhaps I misunderstood. Is the issue with the key going away (off the stack, in this case) part of the real problem? or an artifact of this example code? If it's part of the real code, can you convince the source of your keys to switch to smart pointers? If not, you simply do have to copy it.
jkerian
One other solution. If I understand correctly, you need to have access to the key when you look up the item in the map again? If so, won't simply using std::map::find work? The iterator that is returned will include the ->first parameter.
jkerian
@jkerian: I guess it's an artifact of my example code. In practice, the user of my library will create a `key`, and then call my interface methods/functions with it. I'm resigned to the fact that at least one copy of the `key` must be made (that's inherent in the act of inserting it into the `map`). But I'd prefer not to incur yet another copy, if possible.
Oli Charlesworth
+1  A: 

You could put the reference in place after insertion, but you'd have to make it a pointer:

std::map<key, value>::iterator iter = m.insert(std::make_pair(k, v)).first;
iter->second.setValue(&iter->first);
Walter Mundt
Oli Charlesworth
Yeah, it is, or at least as long as that entry in m lasts. If you remove the entry from the map, the whole thing gets freed, though, including the value. You might not be able to get at the key from the value destructor though.
Walter Mundt
You said you get your key from elsewhere; note that putting the key object in the map copies it, because the map stores actual data in its tree nodes, not just pointers. You can define a type that just holds a pointer or reference and passes comparison through if you have to, of course.
Walter Mundt
Also, _technically_ I think you may only be guaranteed that the iterator itself will remain valid, but I think pretty much all implementations serve that requirement in a way that preserves the addresses of the keys and values as well.
Walter Mundt
@Walter: I'm worried that as one puts more items into `m` and it re-balances itself, the key will get copied and the old one destroyed. Does the standard guarantee that this won't happen?
Oli Charlesworth
No, that's not how it works. Rebalancing rearranges the pointers between existing nodes; it doesn't have to actually move the nodes in memory.
Walter Mundt