C++標準ライブラリのハッシュ計算ファンクタstd::hash
を用いてヌル終端文字列のハッシュ計算を行う場合、ポインタ型に対するstd::hash<const char*>
特殊化ではなくstd::hash<std::string_view>
特殊化を用いる。*1
下記コードにあるh0
では、“文字列” からではなくアドレス値からハッシュ計算されてしまう。*2
#include <cassert> #include <functional> #include <string_view> int main() { char str[] = "Hello"; // NG: 意図に反してアドレス値からハッシュ計算 std::hash<const char*> h0; assert( h0(str) != h0("Hello") ); // 変数strと文字列リテラルは異なるアドレス値を持つため // (非常に高確率で)ハッシュ値も不一致となる // OK: string_view経由で文字列のハッシュ計算 std::hash<std::string_view> h1; assert( h1(str) == h1("Hello") ); // 文字列"Hello"から同一ハッシュ値が算出される }
おまけ:2つの文字列リテラルが異なるアドレス値を持つかは未規定。*3
std::hash<const char*> h0; const char* str = "Hello"; assert( h0(str) != h0("Hello") ); // ??
関連URL
*1:セマンティクス上は std::hash<std::string> でも期待通り動作するが、std::string 型の一時オブジェクト構築オーバーヘッドが生じる。C++11/14 時点では std::string_view が提供されないため、選択肢は std::hash<std::string> のみ。
*2:低品質なC++標準ライブラリ実装もしくは偶発的なハッシュ衝突によって、 h0(str) == h0("Hello") となる可能性もゼロではない。こんなところで運を使わない(・ε・ )
*3:C++20 5.13.5/p14: "Whether all string-literals are distinct (that is, are stored in non overlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified."