我正在开发一个使用无序容器的(C ++)库。这些对于它们存储的元素类型需要一个哈希(通常是模板结构std::hash
的特化)。在我的例子中,这些元素是封装字符串文字的类,类似于the bottom of this page示例的conststr
。 STL为常量char指针提供了一个特殊化,然而,它只计算指针,如here, in the 'Notes' section所述:
C字符串没有专业化。
std::hash<const char*>
它产生一个指针值(内存地址)的哈希值 不会检查任何字符数组的内容。
虽然这很快(或者我认为),但是C ++标准并不能保证在同一地址存储几个相等的字符串文字,如this question中所述。如果不是,则不会满足哈希的第一个条件:
对于两个相等的参数k1和k2,
std::hash<Key>()(k1) == std::hash<Key>()(k2)
如果给出上述保证,我想使用提供的特化选择性地计算哈希,否则我想使用其他算法。虽然回过头来询问那些包含我的标题或构建我的库的人来定义一个特定的宏是可行的,但是定义一个实现更可取。
在任何C ++实现中是否有任何宏,但主要是g ++和clang,其定义保证在同一地址存储多个相等的字符串文字?
一个例子:
#ifdef __GXX_SAME_STRING_LITERALS_SAME_ADDRESS__
const char str1[] = "abc";
const char str2[] = "abc";
assert( str1 == str2 );
#endif
答案 0 :(得分:6)
在任何C ++实现中是否有任何宏,但主要是g ++和clang,其定义保证在同一地址存储多个相等的字符串文字?
-fmerge-constants
option(不保证):尝试跨编译单元合并相同的常量(字符串常量和浮点常量)。
如果汇编器和链接器支持,则此选项是优化编译的默认选项。使用-fno-merge-constants可以抑制此行为。
在-O,-O2,-O3,-Os等级启用。
字符串池允许多个缓冲区的多个指针作为指向单个缓冲区的多个指针。在以下代码中,s和t使用相同的字符串进行初始化。字符串池使它们指向相同的内存:
char *s = "This is a character buffer";
char *t = "This is a character buffer";
注意:虽然MSDN使用char*
字符串文字,但应使用const char*
-fmerge-constants
选项,但除了--help
部分外,我找不到太多相关内容,所以我不确定是否真的相当于gcc的一个:禁止合并常量
无论如何,字符串文字的存储方式依赖于实现(很多都将它们存储在程序的只读部分中)。
不是在可能的依赖于实现的黑客上构建库,而是建议使用std::string
而不是C风格的字符串:它们的行为与您期望的完全相同。 / p>
您可以使用std::string
方法在容器中就地构建emplace()
:
std::unordered_set<std::string> my_set;
my_set.emplace("Hello");
答案 1 :(得分:2)
虽然C ++似乎不允许任何适用于字符串文字的方法,但如果你不介意将字符串文字重写为字符序列,那么解决这个问题就会有一种丑陋但有些可行的方法。
template <typename T, T...values>
struct static_array {
static constexpr T array[sizeof...(values)] { values... };
};
template <typename T, T...values>
constexpr T static_array<T, values...>::array[];
template <char...values>
using str = static_array<char, values..., '\0'>;
int main() {
return str<'a','b','c'>::array != str<'a','b','c'>::array;
}
这需要返回零。编译器必须确保即使多个转换单元实例化str<'a','b','c'>
,这些定义也会合并,并且您最终只会得到一个数组。
但是,您需要确保不要将其与字符串文字混合使用。任何字符串文字都保证不与任何模板实例化的数组相等。
答案 2 :(得分:1)
tacklelib
C++11
库具有一个带有tmpl_string
类的宏,用于将文字字符串作为模板类实例保存。 tmpl_string
包含一个具有相同内容的静态字符串,该字符串可确保同一模板类实例的地址相同。
测试:
https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/src/tests/unit/test_tmpl_string.cpp
示例:
const auto s = TACKLE_TMPL_STRING(0, "my literl string")
我在另一个宏中使用了它,以方便且一致地提取文字字符串begin / end:
#include <tacklelib/tackle/tmpl_string.hpp>
#include <tacklelib/utility/string_identity.hpp>
//...
std::vector<char> xml_arr;
xml_arr.insert(xml_arr.end(), UTILITY_LITERAL_STRING_WITH_BEGINEND_TUPLE("<?xml version='1.0' encoding='UTF-8'?>\n"));