C++ – correct idiom for std::string constants

cconstantsstring

I have a map that represents a DB object. I want to get 'well known' values from it

 std::map<std::string, std::string> dbo;
 ...
 std::string val = map["foo"];

all fine but it strikes me that "foo" is being converted to a temporary string on every call. Surely it would be better to have a constant std::string (of course its probably a tiny overhead compared to the disk IO that just fetched the object but its still a valid question I think). So what is the correct idiom for std::string constants?

for example – I can have

 const std::string FOO = "foo";

in a hdr, but then I get multiple copies

EDIT: No answer yet has said how to declare std::string constants. Ignore the whole map, STL, etc issue. A lot of code is heavily std::string oriented (mine certainly is) and it is natural to want constants for them without paying over and over for the memory allocation

EDIT2: took out secondary question answered by PDF from Manuel, added example of bad idiom

EDIT3: Summary of answers. Note that I have not included those that suggested creating a new string class. I am disappointed becuase I hoped there was a simple thing that would work in header file only (like const char * const ). Anyway

a) from Mark b

 std::map<int, std::string> dict;
 const int FOO_IDX = 1;
 ....
 dict[FOO_IDX] = "foo";
 ....
 std:string &val = dbo[dict[FOO_IDX]];

b) from vlad

 // str.h
 extern const std::string FOO;
 // str.cpp
 const std::string FOO = "foo";

c) from Roger P

 // really you cant do it

(b) seems the closest to what I wanted but has one fatal flaw. I cannot have static module level code that uses these strings since they might not have been constructed yet. I thought about (a) and in fact use a similar trick when serializing the object, send the index rather than the string, but it seemed a lot of plumbing for a general purpose solution. So sadly (c) wins, there is not simple const idiom for std:string

Best Answer

The copying and lack of "string literal optimization" is just how std::strings work, and you cannot get exactly what you're asking for. Partially this is because virtual methods and dtor were explicitly avoided. The std::string interface is plenty complicated without those, anyway.

The standard requires a certain interface for both std::string and std::map, and those interfaces happen to disallow the optimization you'd like (as "unintended consequence" of its other requirements, rather than explicitly). At least, they disallow it if you want to actually follow all the gritty details of the standard. And you really do want that, especially when it is so easy to use a different string class for this specific optimization.

However, that separate string class can solve these "problems" (as you said, it's rarely an issue), but unfortunately the world has number_of_programmers + 1 of those already. Even considering that wheel reinvention, I have found it useful to have a StaticString class, which has a subset of std::string's interface: using begin/end, substr, find, etc. It also disallows modification (and fits in with string literals that way), storing only a char pointer and a size. You have to be slightly careful that it's only initialized with string literals or other "static" data, but that is somewhat mitigated by the construction interface:

struct StaticString {
  template<int N>
  explicit StaticString(char (&data)[N]); // reference to char array
  StaticString(StaticString const&); // copy ctor (which is very cheap)

  static StaticString from_c_str(char const* c_str); // static factory function
  // this only requires that c_str not change and outlive any uses of the
  // resulting object(s), and since it must also be called explicitly, those 
  // requirements aren't hard to enforce; this is provided because it's explicit
  // that strlen is used, and it is not embedded-'\0'-safe as the
  // StaticString(char (&data)[N]) ctor is

  operator char const*() const; // implicit conversion "operator"
  // here the conversion is appropriate, even though I normally dislike these

private:
  StaticString(); // not defined
};

Use:

StaticString s ("abc");
assert(s != "123"); // overload operators for char*
some_func(s); // implicit conversion
some_func(StaticString("abc")); // temporary object initialized from literal

Note the primary advantage of this class is explicitly to avoid copying string data, so the string literal storage can be reused. There's a special place in the executable for this data, and it is generally well optimized as it dates back from the earliest days of C and beyond. In fact, I feel this class is close to what string literals should've been in C++, if it weren't for the C compatibility requirement.

By extension, you could also write your own map class if this is a really common scenario for you, and that could be easier than changing string types.