我在项目中添加了以下函数模板,一位用户抱怨它无法在他们的系统上编译:
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();
}
g++ 9.4
的错误消息大约为
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;
因此,在用T = std::filesystem::path
实例化上述函数模板时出现了错误。我进行了一些调查,发现用其他类型实例化它时没有问题,例如基本类型或std::string
,但仅用std::filesystem::path
。
使用编译器资源管理器,我查看了不同的编译器版本如何处理代码,发现只有g++ v.12
可以使用std::filesystem::path
编译实例化。任何低于12的g++
版本都会出现上述错误而失败。即使在最新版本(14(上,clang
也会产生类似的错误(call to implicitly deleted default constructor
(。我没有测试其他编译器。
我使用的解决方法是用std::set
代替std::unordered_set
。然后在CCD_ 12和CCD_。
所以我想这个错误是std::filesystem::path
的哈希函数丢失了吗?还是我有错?
std::filesystem::path
的std::hash
专业化最近才作为LWG问题3657的解决方案添加到标准草案中。在已发布的C++17和C++20标准中还没有出现这种情况。
然而,一直有一个函数std::filesystem::hash_value
,您可以从中轻松创建一个函数对象,作为散列函数传递给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;
如果您提供该模板时没有任何保证它适用于定义了std::hash
专门化的类型以外的类型,那么我认为您没有问题。
但是,如果您要求类型是可散列的,那么最好让用户以std::unordered_set
的方式重写散列函数。这同样适用于所使用的等式函子。