std::unordered_set<std::filesystem::path>在旧GCC/Clang编译失败:Bug还是用户错误?
问题
我在项目中添加了如下函数模板,有用户反馈在他们的系统上无法编译:
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能成功编译该实例化,所有低于v12的GCC版本都会报上述错误;即使是最新版本(14)的Clang也会产生类似错误(call to implicitly deleted default constructor)。
我采用的临时解决方法是将std::unordered_set替换为std::set,此时在g++ v.8及Clang v.7以上版本均可正常编译。
请问该错误是因为缺少std::filesystem::path的哈希函数导致的,还是我的代码存在问题?
回答
这个错误的核心原因是标准库对std::filesystem::path的哈希函数支持存在版本差异,你的代码本身没有问题。
具体细节:
- C17标准引入
std::filesystem::path,但直到C20才强制要求标准库提供std::hash<std::filesystem::path>的特化实现。在此之前,不同编译器的支持情况不一致:- GCC在v12及以上版本才默认提供该哈希特化;低于v12的版本中,
std::hash<std::filesystem::path>要么未定义,要么被标记为删除,导致依赖它的std::unordered_set<T>无法默认构造。 - Clang至今(v14)仍未默认提供该哈希特化,因此会触发类似错误。
- GCC在v12及以上版本才默认提供该哈希特化;低于v12的版本中,
改用std::set能正常编译的原因是:std::set依赖的是std::less<T>(即<运算符),而std::filesystem::path从C17开始就支持<运算符重载,所以在符合C17标准的编译器版本中都能正常工作。
如果想继续使用std::unordered_set兼容旧编译器,有两个可选方案:
- 为
std::filesystem::path自定义哈希函数,显式传递给std::unordered_set:
struct PathHash { std::size_t operator()(const std::filesystem::path& p) const { return std::hash<std::string>{}(p.string()); } }; // 使用时指定哈希函数 std::unordered_set<std::filesystem::path, PathHash> seen;
- 升级编译器到GCC v12及以上版本,利用标准库提供的哈希特化。
内容的提问来源于stack exchange,提问作者RL-S
相关产品推荐
相关产品推荐

