Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

std::unordered_set<std::filesystem::path>: compile error on clang and g++ below v.12. Bug or user error?

I added the following function template to my project, and a user complained that it wouldn’t compile on their system anymore:

template<typename T>
std::size_t removeDuplicates(std::vector<T>& vec)
{
    std::unordered_set<T> seen;

    auto newEnd = std::remove_if(
        vec.begin(), vec.end(), [&seen](const T& value)
        {
            if (seen.find(value) != std::end(seen))
                return true;

            seen.insert(value);
            return false;
        }
    );

    vec.erase(newEnd, vec.end());

    return vec.size();
}

The error message with g++ 9.4 was approximately

error: use of deleted function
'std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set() 
[with
_Value = std::filesystem::__cxx11::path; 
_Hash = std::hash<std::filesystem::__cxx11::path>; 
_Pred = std::equal_to<std::filesystem::__cxx11::path>; 
_Alloc = std::allocator<std::filesystem::__cxx11::path>]'
   12 |  std::unordered_set<T> seen;

So the error arose when instantiating the above function template with T = std::filesystem::path.
I investigated a little and found that there was no issue when instantiating it with other types, e.g. fundamental types or std::string, but only with std::filesystem::path.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Using Compiler Explorer, I looked at how different compiler versions treat the code and found that only g++ v.12 can compile the instantiation with std::filesystem::path. Any g++ version below 12 fails with the above error. clang produces a similar error (call to implicitly deleted default constructor), even on the newest version (14). I didn’t test other compilers.

The workaround I used was substituting std::unordered_set with std::set. Then it works on g++ v.8 and clang v.7 and above.

So I guess the error is a missing hashing function for std::filesystem::path? Or is it an error on my part?

>Solution :

The std::hash specialization for std::filesystem::path has only recently been added as resolution of LWG issue 3657 into the standard draft. It hasn’t been there in the published C++17 and C++20 standards.

There has always been however a function std::filesystem::hash_value from which you can easily create a function object to pass as hash function to std::unordered_set:

struct PathHash {
    auto operator()(const std::filesystem::path& p) const noexcept {
        return std::filesystem::hash_value(p);
    }
};

//...

std::unordered_set<std::filesystem::path, PathHash> seen;

If you are providing that template without any guarantees that it works on types other than those that have a std::hash specialization defined, then I think there is no problem on your part.

However, if you require the type to be hashable, it would be a good idea to let the user override the hash function used the same way std::unordered_set does. The same also applies to the equality functor used.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading