2

I'm using a std::map with const std::string keys, and I thought it would be nice to avoid pushing the keys all around the stack, so I changed the key type to pointer:

class less_on_star : less<const string*> {
  public:
    virtual bool operator() (const string* left, const string *right);
};

less_on_star::operator() (const string* left, const string *right) {
  return *left < *right;
}

class Foo {
  private:
    map<const string*, Bar*, less_on_star> bars;
}

It worked fine for a while, then I started getting segfaults in which the string key had lost its guts. The _M_p field pointed to NULL or 0x2, but the keys were always intact when I inserted into the map:

bars[new string(on_stack_string)] = bar;

In gdb it looked like new string(on_stack_string) was pointing the _M_p field to a normal heap location, not a stack value. Is there something special about std::string that it can't be used in data structures like this? Maybe I did something else dumb with the keys, but I can't think what it would be.

Byron Hawkins
  • 2,536
  • 2
  • 24
  • 34
  • 2
    `std::string` internally uses heap allocated memory for the character data. In g++, `sizeof(std::string)` is 24. So when you "thought it would be nice to avoid pushing the keys all around the stack" you were most certainly thinking about something else. As for this error, it probably has something to do with g++'s copy on write. I bet the non-pointer string leaves scope and deletes the heap data since the pointer string is only ever used as a temporary and never written to. – KitsuneYMG Mar 09 '14 at 00:56
  • How are you keeping up with the strings? Seems like you may not be properly refcounting the strings. Maybe used shared_ptr< instead. I would personally prefer to store the string itself as a key. The only negative of this I can think of is that you may have to construct a string for each ::find. – Steger Mar 09 '14 at 00:56
  • No @KitsuneYMG, I wasn't thinking of something else. I asked the question here because I do not know how it works. So what I need is for someone to explain it to me, not to chastise me for not knowing the very thing I am asking about. If I knew how it works, why would I ask how it works? – Byron Hawkins Mar 09 '14 at 02:01

1 Answers1

0

This calls for a flyweight, really:

#include <boost/flyweight.hpp>
#include <map>

typedef boost::flyweight<std::string> string;

struct Bar
{
};

struct Foo {
    std::map<string, Bar*> bars;
};

int main()
{
    Foo foo;
    foo.bars.insert(string("hello"), new Bar());
    foo.bars.insert(string("world"), new Bar());
}

Now, in order to have some safety on the Bar* elements, why don't you even use boost::ptr_map?

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Why would you ever do this? `map` doesn't use address-comparison and strings are lightweight. Also, there's no `c++11` tag here. thirdly, since the keys are only copied the first time they are inserted (otherwise key is left alone and it's just the value that's changed), using flyweight like this is just going result is a lot of extra copies AND a second map holding all the keys, right? – KitsuneYMG Mar 09 '14 at 01:13
  • @KitsuneYMG Nope. You have got the expectations wrong. Also, there's nothing C++11 specific there. – sehe Mar 09 '14 at 01:14
  • 1
    `using string = ...` and `emplace` are c++11 specific. – KitsuneYMG Mar 09 '14 at 01:16
  • If you want to be even quicker, see this answer of mine **[Implementing a “string pool” that is guaranteed not to move](http://stackoverflow.com/questions/20938441/implementing-a-string-pool-that-is-guaranteed-not-to-move/20961180#20961180)** (but it's a lot less general and flexible) – sehe Mar 09 '14 at 01:16
  • 4
    @KitsuneYMG Well, thanks for pointing that out. c++11 is the current version of the language. We're in 2014 actually, and all compilers support this. Unless a question is tagged [tag:c++03] we all assume c++11 support (maybe not some things that e.g. MSVC doesn't support) – sehe Mar 09 '14 at 01:17
  • @KitsuneYMG, I can't see the [tag:c++03] tag either. So what version is it? – Shoe Mar 09 '14 at 01:19
  • 1
    @sehe And yet, people who ask questions dealing with new features tag their questions `c++11`. It's almost as if most compilers aren't yet c++ compilers and new language features aren't in common use with most people. For someone with such a high rep, I'd expect some actual intelligence and reasoning skills. Also, moving the goal posts much? "There's nothing c++11 specific there"? really? – KitsuneYMG Mar 09 '14 at 20:00
  • @KitsuneYMG Trololol. Nice reasoning skills you have there. As far as I can see you're just opposing my observations with your own, and then adding a _ad hominem_. Yawn, subjectivity. Also, there was ***nothing*** c++11 specific to my suggestion, indeed. It took me all of 7 seconds to change the `using` to a `typedef` and `s/emplace/insert/`. Yawn again. (What are you even here for? Where's your answer? Just downvote and/or move along?) – sehe Mar 09 '14 at 23:06