'std::filesystem::p ath' 没有标准哈希吗?
Is there no standard hash for `std::filesystem::path`?
我有一个简单的程序,旨在存储一组C++17std::filesystem::path
对象。 既然有一个std::filesystem::hash_value
是标准的一部分,为什么在我不必提供自己的std::hash
的情况下编译此代码?
当我使用 gcc 8.1.1 作为g++ -std=c++17 -NO_HASH=1 hashtest.cpp -o hashtest -lstdc++fs
进行编译和链接时,我的哈希函数包含在内,一切都运行良好。 但是,如果我将其更改为-NO_HASH=0
,我会收到一长串错误消息,其中关键之一是:
usr/include/c++/8/bits/hashtable.h:195:21: error: static assertion failed: hash function must be invocable with an argument of key type
static_assert(__is_invocable<const _H1&, const _Key&>{},
如果你想玩,这里有一个现场的Coliru版本。
真的没有定义std::hash<std::filesystem::path>
吗?我错过了什么?
对于那些对我为什么想要这样的东西感兴趣的人,它是这样的:https://codereview.stackexchange.com/questions/124307/from-new-q-to-compiler-in-30-seconds
哈希测试.cpp
#include <optional>
#include <unordered_set>
#include <filesystem>
#include <string>
#include <iostream>
namespace fs = std::filesystem;
#if NO_HASH
namespace std {
template <>
struct hash<fs::path> {
std::size_t operator()(const fs::path &path) const {
return hash_value(path); }
};
}
#endif
int main()
{
using namespace std::literals;
std::unordered_set< std::optional<fs::path> > paths = {
"/usr/bin"s, std::nullopt, "/usr//bin"s, "/var/log"s
};
for(const auto& p : paths)
std::cout << p.value_or("(no path)") << ' ';
}
既然有一个
std::filesystem::hash_value
是标准的一部分,为什么在我不必提供自己的std::hash
的情况下编译此代码?
是的,有一个fs::hash_value()
但没有std::hash<fs::path>
专业化,这是你需要的。这就是它不编译的原因。至于为什么库提供前一个函数而不是后者,我将引用 Billy O'Neal(MSVC 标准库的实现者(:
看起来像一个缺陷。
但是,将路径作为键放在哈希表中几乎肯定是不正确的;在大多数此类情况下,您需要测试路径等效性。也就是说,
"/foo/bar/../baz"
和"/foo/baz"
是相同的目标,但路径不同。同样,"./bar"
和"./bar"
可能是不同的路径,具体取决于第一个上下文中current_path
的值与第二个上下文中的值。
如果你想要的是规范上唯一的路径,那么std::unordered_set<fs::path>
无论如何都不会做你想做的事。所以也许编译失败不是一件坏事?我对文件系统的了解还不够多,无法以一种或另一种方式说出来。
请注意,您自己不允许为fs::path
提供std::hash
专用化 - 您只能为您控制的类型向std
添加专用化。将称为"程序定义类型"的类型。fs::path
不是您控制的类型,因此您无法专门针对它进行std::hash
。
解决方案是显式编写和使用我自己的哈希。
#include <optional>
#include <unordered_set>
#include <filesystem>
#include <string>
#include <iostream>
namespace fs = std::filesystem;
struct opt_path_hash {
std::size_t operator()(const std::optional<fs::path>& path) const {
return path ? hash_value(path.value()) : 0;
}
};
int main()
{
using namespace std::literals;
std::unordered_set< std::optional<fs::path>, opt_path_hash > paths = {
"/usr/bin"s, std::nullopt, "/usr//bin"s, "/var/log"s
};
for(const auto& p : paths)
std::cout << p.value_or("(no path)") << 'n';
}
这将生成以下输出,正确折叠两个版本的"/usr/bin"
:
"/var/log"
"(no path)"
"/usr/bin"
namespace hashing {
namespace adl {
template<class T, class...Ts>
auto hash_value( T const& t, Ts&&... )
-> std::result_of_t< std::hash<T>&&(T const&) >
{
return std::hash<T>{}(t);
}
template<class T>
auto hasher_private( T const& t )
-> decltype( hash_value( t ) )
{ return hash_value(t); }
}
struct smart_hasher {
template<class T>
auto operator()( T const& t ) const
->decltype( adl::hasher_private( t ) )
{ return adl::hasher_private( t ); }
};
};
所以hashing::smart_hasher
是一个哈希对象,它将在T
的命名空间中查找hash_value(T const&)
,如果失败,将使用std::hash<T>
如果可用,否则将生成编译器错误。
如果要为std
类型编写其他哈希器,请在hashing::adl
中创建hash_value
函数重载。 对于其他类型,请在其关联的命名空间中创建它。 例如,如果要支持tuple
s的哈希处理:
namespace hashing::adl {
template<class...Ts>
std::size_t hash_value( std::tuple<Ts...> const& tup ) {
// get hash values and combine them here
// use `smart_hasher{}( elem ) to hash each element for
// recursive smart hashing
}
}
现在,任何使用smart_hasher
的人都会自动为提供该自定义的任何内容选择哈希器。
- 对于短字符串来说,这是一个很好的哈希函数吗?
- 标准库容器最简单、性能差的哈希类是什么?
- 我可以比朴素哈希表更快地将随机字符串映射到两个类吗?
- 'std::filesystem::p ath' 没有标准哈希吗?
- 标准::C++ 中的无序列图中的自定义哈希
- 如果我放弃哈希计算,我必须调用 SHA1_Final() 吗?
- C 标准不为此类型提供哈希
- 具有完美哈希函数的哈希表比数组好吗?
- C 中的unordered_map/set的哈希功能更快吗?
- 如何对标准::正则表达式进行哈希处理
- 标准::unordered_map中的键与哈希
- C 字符串哈希散列字符串或内存地址吗?
- 错误 c2338 C++ 标准不提供此类型的哈希
- 可视 C2338:C++ 标准不为此类型提供带有字符串和unique_ptr的哈希
- 查找表是哈希表的一种形式吗
- 我可以(可移植)访问C++标准库的哈希实现吗?
- 标准库在实践中如何实现哈希表
- 我的哈希函数有问题吗?
- 如何在大量无损压缩图像中挑选重复的图像对(完全相同)?如何在内存中输入标准::哈希
- 使用STL的哈希列表-我可以将STL列表中的一个项指向另一个项吗?