std::string 常量的正确习惯用法?
我有一个代表数据库对象的地图。我想从中获得“众所周知”的值,
std::map<std::string, std::string> dbo;
...
std::string val = map["foo"];
但让我惊讶的是“foo”在每次调用时都被转换为临时字符串。当然,拥有一个常量 std::string 会更好(当然,与刚刚获取对象的磁盘 IO 相比,它可能是一个很小的开销,但我认为这仍然是一个有效的问题)。那么 std::string 常量的正确习惯用法是什么?
例如 - 我可以
const std::string FOO = "foo";
在 hdr 中拥有,但随后我会得到多个副本
编辑:还没有答案说明如何声明 std::string 常量。忽略整个地图、STL等问题。很多代码都是面向 std::string 的(我的当然是),很自然地想要它们的常量,而不需要一次又一次地为内存分配付出代价
EDIT2:从 Manuel 中取出 PDF 回答的第二个问题,添加了错误的例子习语
EDIT3:答案摘要。请注意,我没有包含那些建议创建新字符串类的内容。我很失望,因为我希望有一个简单的东西只能在头文件中工作(比如 const char * const )。无论如何,
a)来自 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)来自 vlad
// str.h
extern const std::string FOO;
// str.cpp
const std::string FOO = "foo";
c)来自 Roger P
// really you cant do it
(b)似乎最接近我想要的,但有一个致命的缺陷。我无法拥有使用这些字符串的静态模块级代码,因为它们可能尚未构造。我考虑过(a),实际上在序列化对象时使用了类似的技巧,发送索引而不是字符串,但对于通用解决方案来说,这似乎需要很多工作。遗憾的是 (c) 获胜,std:string 没有简单 const 习惯用法
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
复制和缺乏“字符串文字优化”正是 std::strings 的工作方式,并且您无法准确得到您所要求的内容。部分原因是虚拟方法和 dtor 被明确避免。无论如何,如果没有这些,std::string 接口就会变得非常复杂。
该标准要求 std::string 和 std::map 都有特定的接口,而这些接口恰好不允许您想要的优化(作为其其他要求的“意外结果”,而不是明确的)。至少,如果您想真正遵循该标准的所有细节,他们不允许这样做。您确实希望如此,尤其是当使用不同的字符串类来进行此特定优化非常容易时。
然而,那个单独的字符串类可以解决这些“问题”(正如你所说,这很少是一个问题),但不幸的是世界上已经有
number_of_programmers + 1
这样的问题了。即使考虑到轮子的重新发明,我发现拥有一个 StaticString 类很有用,它有 std::string 接口的子集:使用 begin/end、substr、find 等。它也不允许修改(并且适合字符串文字)这样),只存储一个字符指针和一个大小。您必须稍微小心,它仅使用字符串文字或其他“静态”数据进行初始化,但是构造接口在一定程度上缓解了这种情况:使用:
注意此类的主要优点是明确避免复制字符串数据,因此字符串文字存储可以重复使用。这些数据在可执行文件中有一个特殊的位置,并且它通常得到了很好的优化,因为它可以追溯到 C 的早期及更早的时期。事实上,我觉得这个类很接近 C++ 中的字符串文字,如果不是为了 C 兼容性要求的话。
通过扩展,如果这对您来说确实是常见的场景,您也可以编写自己的地图类,并且这可能比更改字符串类型更容易。
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:Use:
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.
很简单:
在标头和
相应的
.cpp
文件中使用。It's simple: use
in your header, and
in the appropriate
.cpp
file.当您想要的只是一个常量字符串时,可以避免创建
std::string
的开销。但是您需要为此编写一个特殊的类,因为 STL 或 Boost 中没有类似的内容。或者更好的选择是使用 Chromium 中的StringPiece
或 LLVM 中的StringRef
等类。请参阅此相关主题 了解更多信息。如果您决定继续使用
std::string
(您可能会这样做),那么另一个不错的选择是使用 Boost MultiIndex 容器,它具有以下功能(引用 文档):<块引用>
Boost MultiIndex [...] 提供查找
接受搜索键的操作
与 key_type 不同
索引,这是一个特别有用的
当 key_type 对象为
创建成本昂贵。
带有昂贵钥匙的地图 作者:Andrei Alexandrescu (< em>C/C++ Users Journal,2006 年 2 月)与您的问题相关,是一本非常好的读物。
It's possible to avoid the overhead of creating a
std::string
when all you want is a constant string. But you'll need to write a special class for that because there's nothing similar in the STL or in Boost. Or a better alternative is to use a class likeStringPiece
from Chromium orStringRef
from LLVM. See this related thread for more info.If you decide to stay with
std::string
(which you probably will) then another good option is to use the Boost MultiIndex container, which has the following feature (quoting the docs):Maps with Expensive Keys by Andrei Alexandrescu (C/C++ Users Journal, Feb. 2006) is related to your problem and is a very good read.
正确的习语是您正在使用的习语。 99.99% 的情况下,无需担心 std::string 构造函数的开销。
我确实想知道 std::string 的构造函数是否可以被编译器转换为内部函数?理论上这是可能的,但我上面的评论足以解释为什么它没有发生。
The correct idiom is the one you're using. 99.99% of the time there is no need to worry about the overhead of std::string's constructor.
I do wonder if std::string's constructor could be turned into an intrinsic function by a compiler? Theoretically it might be possible, but my comment above would be explanation enough for why it hasn't happened.
看来您已经知道运行时的字符串文字是什么,因此您可以在枚举值和字符串数组之间设置内部映射。然后,您将在代码中使用枚举而不是实际的 const char* 文字。
然后你会说
map[getString(MAP_STRING)]
或类似的内容。另外,如果不需要修改返回值,还可以考虑通过 const 引用存储返回值,而不是复制:
It appears that you already know what the string literals will be at runtime, so you can set up an internal mapping between enumerated values and an array of strings. Then you would use the enumeration instead of an actual const char* literal in your code.
Then you would say
map[getString(MAP_STRING)]
or similar.As an aside, also consider storing the return value by const reference rather than copy if you don't need to modify it:
在 C++14 中你可以这样做
In C++14 you can do
问题在于 std::map 将键和值复制到它自己的结构中。
您可以有一个
std::map
,但您必须提供功能对象(或函数)来比较键和值数据,因为此模板是供指点。默认情况下,map
将比较指针,而不是指针指向的数据。权衡是一次性复制 (
std::string
) 与访问比较器 (const char *
)。另一种选择是编写您自己的
map
函数。The issue is that
std::map
copies the key and values into its own structures.You could have a
std::map<const char *, const char *>
, but you would have to provide functional objects (or functions) to compare the key and value data, as this stencil is for pointers. By default, themap
would compare pointers and not the data the pointers point to.The trade off is one-time copy (
std::string
) versus accessing a comparator (const char *
).Another alternative is to write your own
map
function.我认为你正在寻找的是 'boost::flyweight < std::string > '
这是对共享字符串值的逻辑常量引用。非常高效的存储和高性能。
I think what you're looking for is 'boost::flyweight < std::string > '
this is a logically const reference to a shared string value. very efficient storage and high performance.
我的解决方案(优点是能够使用之前回答此问题时不存在的 C++11 功能):
是的,它是一个宏,并且可以使用更好的名称。
My solution (having the advantage of being able to use C++11 features that weren't present when this question was previously answered):
Yes, it's a macro and it could use a better name.