std :: string常量的正确习惯用法?

pm1*_*100 24 c++ string constants

我有一个代表DB对象的地图.我希望从中得到"众所周知"的价值观

 std::map<std::string, std::string> dbo;
 ...
 std::string val = map["foo"];
Run Code Online (Sandbox Code Playgroud)

一切都很好但是我觉得"foo"在每次通话时都被转换为临时字符串.当然,拥有一个常量的std :: string会更好(当然,与刚刚获取对象的磁盘IO相比,它可能只是一个很小的开销,但我认为它仍然是一个有效的问题).那么std :: string常量的正确习惯是什么?

例如 - 我可以

 const std::string FOO = "foo";
Run Code Online (Sandbox Code Playgroud)

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

编辑:还没有回答说如何声明std :: string常量.忽略整个地图,STL等问题.很多代码都是以std :: string为导向的(我当然是这样)并且很自然地需要为它们设置常量而不需要为内存分配反复付费

EDIT2:从曼努埃尔手中接过PDF回答的二级问题,添加了一个不好习惯的例子

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

a)来自马克b

 std::map<int, std::string> dict;
 const int FOO_IDX = 1;
 ....
 dict[FOO_IDX] = "foo";
 ....
 std:string &val = dbo[dict[FOO_IDX]];
Run Code Online (Sandbox Code Playgroud)

b)来自vlad

 // str.h
 extern const std::string FOO;
 // str.cpp
 const std::string FOO = "foo";
Run Code Online (Sandbox Code Playgroud)

c)来自Roger P.

 // really you cant do it
Run Code Online (Sandbox Code Playgroud)

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

小智 18

复制和缺少"字符串文字优化"就是std :: strings如何工作,你无法得到你所要求的.部分原因是因为明确避免了虚方法和dtor.该的std :: string接口是很多没有那些复杂的,反正.

该标准需要std :: string和std :: map的某个接口,并且这些接口碰巧不允许你想要的优化(作为其他要求的"意外后果",而不是明确的).至少,如果你想真正遵循标准的所有细节,他们不允许它.而且你确实希望这样,特别是当这种特定优化使用不同的字符串类时非常容易.

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

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
};
Run Code Online (Sandbox Code Playgroud)

使用:

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

请注意,此类的主要优点是显式避免复制字符串数据,因此可以重用字符串文字存储.这个数据的可执行文件中有一个特殊的位置,它通常都经过了很好的优化,因为它可以追溯到C及以后的最早期.事实上,我认为这个类接近于C++中应该使用的字符串文字,如果它不符合C兼容性要求.

通过扩展,如果这对您来说是一个非常常见的场景,您也可以编写自己的地图类,这可能比更改字符串类型更容易.

  • 如果有足够的需求,我可以从头开始重新发明这个类,在SO的开源许可下加入SO(我通常非常小心).假设这个评论得到+10票,那么我会这样做.:) (13认同)
  • @RogerPate 你现在陷入困境 (2认同)

Vla*_*lad 9

这很简单:使用

extern const std::string FOO;
Run Code Online (Sandbox Code Playgroud)

在你的标题中,和

const std::string FOO("foo");
Run Code Online (Sandbox Code Playgroud)

在适当的.cpp文件中.

  • 对于每个标题(例如,`foo.h`),通常都有一个合适的`.cpp`文件(在我们的例子中是`foo.cpp`).标题可以包含多次,因此它们通常包含数据声明(但通常除了模板类之外).`.cpp`文件包含实际定义. (2认同)

Man*_*uel 6

  1. std::string当你想要的只是一个常量字符串时,可以避免创建开销的开销.但是你需要为此编写一个特殊的类,因为在STL或Boost中没有类似的东西.或者更好的选择是使用StringPiece来自Chromium或StringRefLLVM的类.有关详细信息,请参阅此相关主题.

  2. 如果你决定留下std::string(你可能会这样),那么另一个好的选择是使用Boost MultiIndex容器,它具有以下功能(引用文档):

    Boost MultiIndex [...]提供了接受与索引的key_type不同的搜索键的查找操作,当key_type对象创建起来很昂贵时,这是一个特别有用的工具.

带有昂贵钥匙地图由Andrei Alexandrescu撰写( C/C++用户期刊,2006年2月)与您的问题有关,并且阅读非常好.