我正在开发一个使用无序容器的(C++)库。这些都需要一个哈希(通常是模板结构的特殊化)。std::hash
),用于存储元素的类型。在我的例子中,这些元素是封装了字符串字元的类,类似于 conststr
的例子在 页底. STL提供了一个针对常量char指针的特殊化,然而,它只计算指针,如解释的那样 这里,在 "注释 "部分:
对 C 字符串没有特殊化。
std::hash<const char*>
它产生一个指针值(内存地址)的哈希值,它不检查任何字符数组的内容。
虽然这样做非常快(或者我认为是这样),但是C++标准并不能保证在同一个地址上是否存储了几个相等的字符串字元,正如在下面解释的那样 这个问题. 如果不满足,就不会满足哈希斯的第一个条件。
对于两个参数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
在任何C++实现中,但主要是g++和clang中,是否有任何宏,其定义可以保证几个相等的字符串字元存储在同一个地址?
-fmerge-constants
选择权 (这是 不 担保)。试图 合并相同的常量(字符串常量和浮点常量)。
如果汇编器和链接器支持这个选项,那么这个选项是优化编译的默认选项。使用-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-风格的字符串:它们的表现与你所期望的完全一致。
你可以构建你的 std::string
在您的容器中就地使用 emplace()
方法。
std::unordered_set<std::string> my_set;
my_set.emplace("Hello");
虽然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'>
这些定义会被合并,你最终只得到一个数组。
不过你需要确保你不会把它和字符串文字混合在一起。任何字符串文字都可以保证 不 来与任何一个模板实例的数组进行比较。
的 tacklelib
C++11
库中有一个宏,其内容为 tmpl_string
类作为模板类的实例来保存一个文字字符串。该 tmpl_string
包含一个内容相同的静态字符串,它保证了同一个模板类实例的地址相同。
https:/sourceforge.netptacklelibtacklelibHEADtreetrunkincludetacklelibtackletmpl_string.hpp。
测试。
https:/sourceforge.netptacklelibtacklelibHEADtreetrunksrctestsunittest_tmpl_string.cpp。
例子:我在另一个宏中使用了它,以方便和一致地提取一个字面字符串 beginend: :。
const auto s = TACKLE_TMPL_STRING(0, "my literl string")
我在另一个宏中使用了它,以方便和一致地提取一个字面字符串beginend。
#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"));
https:/sourceforge.netptacklelibtacklelibHEADtreetrunkincludetacklelibutilitystring_identity.hpp。