Почему оператор == 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==
.