7

I have classes, each of them return its name

struct IFoo {
    virtual const char * GetName() const = 0;
}

struct Foo : IFoo {
   const char * GetName() const { return "Foo"; }
}

struct Bar: IFoo {
   const char * GetName() const { return "Bar"; }
}

And somewhere else:

Foo* a = new Foo();
Foo* b = new Foo();

std::map<const char *, int> data;
data[a->GetName()] = 0;
printf("%i", data[b->GetName()]);

string literals should be stored in one place at memory, but is it 100%? This code works in gcc, but I am not sure of its multi-platformness.

manlio
  • 18,345
  • 14
  • 76
  • 126
Martin Perry
  • 9,232
  • 8
  • 46
  • 114
  • 2
    Yes this is safe, in fact provision was specifically added to the C++ standard so that this works – M.M May 13 '17 at 09:21
  • 1
    This is safe, as long as you call `operator []` with results of `GetName()`. If you try calling it with hard-coded `"Foo"` from another translation unit, the result is not guaranteed. On `gcc` it will work, because it merges constant pools by default. – Sergey Kalinichenko May 13 '17 at 09:26
  • 1
    Very related, almost a duplicate: [String Literal address across translation units](http://stackoverflow.com/q/26279628/335858) – Sergey Kalinichenko May 13 '17 at 09:27
  • I wouldn't do that, IMO it's brittle code. If the implementation of the derived class is later changed to return a pointer to a dynamically allocated string, then you may suddenly have two identical strings that compare differently in `std::map` because their pointers are different. – zett42 May 13 '17 at 09:34
  • @dasblinkenlight The standard just doesn't guarantee that, whether across TU or not; "whether successive evaluations of a string-literal yield the same or a different object is unspecified." So the next evaluation of `a->GetName()` might produce a new object, isn't it? – songyuanyao May 13 '17 at 09:35
  • String literals generally dont have the same address. But this is slightly different case imho, because I have the string literal embeded in a class and I am not sure if its instance will always use the same literal or possibly create a new one. I can change key to `std::string`, but it will slow things down, evantually. – Martin Perry May 13 '17 at 09:42
  • @zett42 If it happend, its users fault. In documentation, there is said, that it must return literal. – Martin Perry May 13 '17 at 09:46
  • 1
    Personally I would go with the more robust solution of using `std::string` keys unless you have profiled it to be a major bottleneck. In my experience, short `std::string` keys (say up to 10 characters) aren't significantly slower than pointer keys, especially on 64-bit platforms where pointer is 8 bytes to compare. – zett42 May 13 '17 at 10:49

1 Answers1

5

Is it safe to use const char * literal as a std::map key?

Yes.

However, consider that this is not guaranteed to find your object (but may, depending on implementation):

data["Foo"]

And this is guaranteed to not find your object:

char[] str = "Foo";
data[str];

Using a custom map comparator based on std::strcmp would allow both of the above cases to work.

Then the only trap left is the possibility of storing a pointer to a local buffer into the map that would outlive the local buffer. That's not going to happen if you only store string literals of course, but that's something that you must remember to keep in mind when working with the map. A std::string key would not have such caveat.

eerorika
  • 232,697
  • 12
  • 197
  • 326