std::string 常量的正确习语?

2022-01-23 00:00:00 string constants c++

我有一张代表数据库对象的地图.我想从中获得众所周知"的价值

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"];

一切都很好,但让我感到震惊的是,foo"在每次调用时都被转换为临时字符串.当然,最好有一个常量 std::string (当然,与刚刚获取对象的磁盘 IO 相比,它可能是一个很小的开销,但我认为它仍然是一个有效的问题).那么 std::string 常量的正确用法是什么?

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?

例如-我可以拥有

 const std::string FOO = "foo";

在 hdr 中,但后来我得到了多个副本

in a hdr, but then I get multiple copies

还没有答案说明如何声明 std::string 常量.忽略整个地图、STL 等问题.很多代码都是面向 std::string 的(我的当然是),很自然地需要常量而不为内存分配反复支付

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

从 Manuel 那里取出 PDF 回答的第二个问题,添加了坏习语的例子

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

答案摘要.请注意,我没有包括那些建议创建新字符串类的内容.我很失望,因为我希望有一个简单的东西只能在头文件中工作(比如 const char * const ).反正

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) 来自马克 b

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

b) 来自弗拉德

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

c) 来自 Roger P

c) from Roger P

 // really you cant do it

(b) 似乎最接近我想要的,但有一个致命缺陷.我不能拥有使用这些字符串的静态模块级代码,因为它们可能尚未构建.我考虑过(a)并且实际上在序列化对象时使用了类似的技巧,发送索引而不是字符串,但是对于通用解决方案来说似乎需要很多管道.所以遗憾的是 (c) 赢了,std:string 没有 simple const idiom

(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

推荐答案

复制和缺乏字符串文字优化"正是 std::strings 的工作原理,你无法得到你想要的.部分原因是因为明确避免了虚拟方法和 dtor.无论如何,std::string 接口很多很复杂.

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.

该标准要求 std::string 和 std::map 都有特定的接口,而这些接口碰巧不允许您进行您想要的优化(作为其其他要求的意外后果",而不是明确地).至少,如果您想真正遵循标准的所有细节,他们不允许这样做.而且您确实想要这样,尤其是当使用不同的字符串类进行此特定优化非常容易时.

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.

但是,单独的字符串类可以解决这些问题"(正如您所说,这很少成为问题),但不幸的是,世界上已经有 number_of_programmers + 1 个.即使考虑到轮子的重新发明,我发现拥有一个具有 std::string 接口子集的 StaticString 类很有用:使用 begin/end、substr、find 等.它也不允许修改(并且适合字符串文字这样),只存储一个字符指针和一个大小.您必须稍微小心,它仅使用字符串文字或其他静态"数据进行初始化,但构造接口在一定程度上缓解了这一点:

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-''-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
};

用途:

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

请注意,此类的主要优点是明确避免复制字符串数据,因此可以重用字符串文字存储.这些数据在可执行文件中有一个特殊的位置,并且通常可以很好地优化,因为它可以追溯到 C 的早期及以后.事实上,如果不是出于 C 兼容性要求,我觉得这个类接近于 C++ 中的字符串文字.

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.

相关文章