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.

Reply via email to