I have a simple program that's intended to store a set of C++17 std::filesystem::path
objects. Since there is a std::filesystem::hash_value
that's part of the standard, why doesn't this code compile without me having to supply my own std::hash
?
When I compile and link using gcc 8.1.1 as g++ -std=c++17 -NO_HASH=1 hashtest.cpp -o hashtest -lstdc++fs
my hash function is included and everything operates perfectly. However, if I change it to -NO_HASH=0
, I get a very long list of error messages, the key one of which is this:
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&>{},
Here's a live Coliru version if you'd like to play.
Is there really no defined std::hash<std::filesystem::path>
? What am I missing?
For those who are interested in why I'd want such a thing, it's this: https://codereview.stackexchange.com/questions/124307/from-new-q-to-compiler-in-30-seconds
#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)") << ' ';
}
Defined in header <functional> template< class Key > struct hash; (since C++11)
Yes, std::hash return same result for different std::string . The creation of buckets is different by different compiler.
std::hash class in C++ STLIt is used to get the hash value of the argument that is being passed to it. If the argument doesn't change, the value doesn't change either. Member functions: This Hash class only has one member function: operator(): It returns hashed value for given argument.
A filesystem is a structure for the computer to store the files and folders that make up the data of the operating system. Inside a filesystem, folders are referred to as directories. Folders that exist inside other folders are called subdirectories.
Since there is a
std::filesystem::hash_value
that's part of the standard, why doesn't this code compile without me having to supply my ownstd::hash
?
Right, there is a fs::hash_value()
but there is no specialization of std::hash<fs::path>
, which is what you would need. That's why it doesn't compile. As to why the library provides the former function but not the latter, I'll quote from Billy O'Neal (implementer for MSVC's standard library):
Looks like a defect.
However, putting paths as keys in a hash table is almost certainly incorrect; you need to test for path equivalence in most such scenarios. That is,
"/foo/bar/../baz"
and"/foo/baz"
are the same target but are not the same path. Similarly,"./bar"
and"./bar"
may be different paths, depending on the value ofcurrent_path
in the first context vs. in the second.
If what you want is canonically unique paths, then simply std::unordered_set<fs::path>
wouldn't do what you want anyway. So perhaps it failing to compile isn't a bad thing? I don't know enough about filesystem to say one way or the other.
Note that you, yourself, providing a specialization of std::hash
for fs::path
is not allowed - you can only add specializations to std
for types you control. Types that will be called "program-defined types." fs::path
is not a type you control, so you can't specialize std::hash
for it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With