https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115285
Bug ID: 115285 Summary: std::unordered_set can have duplicate values Product: gcc Version: 15.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: bugs at phlexo dot com Target Milestone: --- When a derived class, which uses its base class' hasher and performs a conversion during the base class constructor call, is used as the value_type of a std::unordered_set object, the std::unordered_set will contain duplicates if it's constructed from iterators of a container holding base class objects that result in duplicate values after the conversion. Example: #include <string> #include <vector> #include <unordered_set> #include <cassert> class TrimmedStr : public std::string { static std::string trim_str(std::string const &str) { auto start = str.find_first_not_of(" \r\n\t"); return start == std::string::npos ? str : str.substr(start, str.find_last_not_of(" \r\n\t") - start + 1); } public: TrimmedStr(std::string const &arg) : std::string{trim_str(arg)} {} TrimmedStr(char const *arg) : TrimmedStr{std::string{arg}} {} }; namespace std { template<> struct hash<TrimmedStr> : public std::hash<std::string> {}; } int main() { assert(TrimmedStr{"foo "} == std::string{"foo"}); assert(TrimmedStr{" foo"} == std::string{"foo"}); assert(TrimmedStr{" foo "} == std::string{"foo"}); std::unordered_set<TrimmedStr> set_from_initializer_list{"foo", "bar", " foo ", " bar "}; assert(set_from_initializer_list.size() == 2); //Passes. std::vector<std::string> args{"foo", "bar", " foo ", " bar "}; std::unordered_set<TrimmedStr> set_from_iterators{args.begin(), args.end()}; assert(set_from_iterators.size() == 2); //Fails. } The final assertion in this code fails with libstdc++ v13 and above, but works as expected with libstdc++, and libstdc++ < v13. Bisecting suggests this was introduced in commit dc9b92facf87a6f2d8b0e5d5fc404f30c3b15a74.