Почему оператор == std::unordered_multiset<T> возвращает неправильный результат, когда T является типом указателя?

Это баг, или я что-то не так делаю? Я уже пытался предоставить функторы хэширования и равенства для типа указателя, но, похоже, это не работает. Я даже пытался создать свой собственный миниатюрный контейнер шаблонов только для проверки функторов.

Хеширующий функтор:

      class CharPtHash
{
private:
    using pChar = char*;
public:
    size_t operator()(const pChar& c) const
    {
        std::hash<char> hasher;
        if (c == nullptr)
        {
            return 0;
        }
        return hasher(*c);
    }
};

Равенство:

      class CharPtEqual
{
private:
    using pChar = char*;
public:
    bool operator()(const pChar& lhs, const pChar& rhs)const
    {

        if (lhs == rhs)//not sure of nullptr is equal to itself.
        {
            return true;
        }
        else if (lhs==nullptr || rhs==nullptr)
        {
            return false;
        }
        return *lhs == *rhs;
    }
};

Основной:

      int main()
{
    cout << "Testing unordered_multiset with keys being simple types:\n";
    unordered_multiset<char> sA1({ 'a','b','c' });
    unordered_multiset<char> sA2({ 'a','c','b' });

    cout << "Values: " << endl << sA1 << endl << sA2 << endl;

    cout << (sA1 == sA2 ? "Equal" : "Not Equal");
    cout << endl;

    cout << "Testing unordered_multiset with keys being pointers to simple types:\n";
    char** c1 = new char* [3]{ new char('a'), new char('b'), new char('c') };
    char** c2 = new char* [3]{ new char('a'), new char('c'), new char('b') };

    unordered_multiset<char*,CharPtHash,CharPtEqual> sB1;
    unordered_multiset<char*,CharPtHash,CharPtEqual> sB2;

    sB1.insert(c1[0]);
    sB1.insert(c1[1]);
    sB1.insert(c1[2]);
    sB2.insert(c2[0]);
    sB2.insert(c2[1]);
    sB2.insert(c2[2]);

    cout << "Values: " << endl << sB1 << endl << sB2 << endl;

    cout << (sB1 == sB2 ? "Equal" : "Not Equal");
    cout << endl;

    cin.get();
}

Я попытался скомпилировать его в С++20 и С++14, используя Visual Studio 2022.

Это результат:

      Testing unordered_multiset with keys being simple types:
Values:
{ a, b, c }
{ a, c, b }
Equal
Testing unordered_multiset with keys being pointers to simple types:
Values:
{ a, b, c }
{ a, c, b }
Not Equal

2 ответа

Что ж, мой предыдущий ответ был совершенно неверным: насколько я могу судить, вашHashи являются правильными. Однако дело в другом.дляstd::unordered_multisetиспользуетstd::is_permutation()для внутреннего сравнения и не предоставляет никакой функции сравнения этого алгоритма, поэтому по умолчанию для этого типа (в данном случаеchar*) используется. Из-за этого, я думаю, есть UB, но я не очень понимаю формулировку там.

Справедливости ради, это выглядит как оплошность в стандарте.operator ==дляstd::unordered_multimapне позволяет сравнивать разные типы карт, поэтому должна быть возможность пройтиPredпример дляstd::is_permutation. Или, может быть, есть причина, чтобы так было, но я ее не вижу.

Предоставление собственныхKeyEqualизменить поведение только внутренне, т.е. вставить новые элементы. Однако на .

Согласно ( std::unordered_multiset) ведет себя так, как если бы каждый эквивалентequal_rangeс сравнивались с .

Потенциально вы можете специализировать поведениеstd::is_permutationдля вашего набора до С++20(это неопределенное поведение с С++20):

      template<>
bool std::is_permutation(
    std::unordered_multiset<char*, CharPtHash, CharPtEqual>::const_iterator l_begin, 
    std::unordered_multiset<char*, CharPtHash, CharPtEqual>::const_iterator l_end, 
    std::unordered_multiset<char*, CharPtHash, CharPtEqual>::const_iterator r_begin)
{
    return std::is_permutation(l_begin, l_end, r_begin, CharPtEqual{});
}

Или просто создайте свойchar*обертка с обычайoperator==.

Другие вопросы по тегам