为什么 std::unordered_multiset<T> 的 == 运算符在 T 为指针类型时返回错误的结果?

Why does the == operator of std::unordered_multiset<T> returns wrong result when T is a pointer type?

提问人:Software Carpenter 提问时间:11/14/2022 最后编辑:Ken WhiteSoftware Carpenter 更新时间:11/14/2022 访问量:93

问:

这是一个错误,还是我做错了什么?我已经尝试为指针类型提供哈希和相等函子,但它似乎不起作用。我甚至尝试创建自己的微型模板容器来测试函子。

哈希函子:

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();
}

我尝试使用 Visual Studio 2022 将其编译为 c++20 和 c++14。

这是输出:

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
C++ Visual-Studio STL 相等性 无序多集

评论

0赞 user12002570 11/14/2022
比较不相关的指针?将指针与不同数组的指针进行比较是否为相等,这是未指定的行为吗?
0赞 Yksisarvinen 11/14/2022
@JasonLiam 好吧,答案是你可以很好地比较它们,所以我不明白为什么在这里提到它。
1赞 Drew Dormann 11/14/2022
为什么你认为结果是错误的?你认为会返回与前一个返回的指针相同的指针吗?它们不是同一个指针。new char('a')new char('a')

答:

1赞 Yksisarvinen 11/14/2022 #1

好吧,我之前的回答是完全错误的:据我所知,你的和是正确的。但是,问题出在其他地方。运算符 == for 使用 std::is_permutation() 在内部执行比较,并且不提供该算法的任何比较函数,因此使用该类型(在本例中)的默认值。我认为有 UB,但我不太明白那里的措辞。HashPredstd::unordered_multisetoperator ==char*

公平地说,这看起来像是标准的疏忽。 for 不允许比较不同类型的映射,因此应该可以将 instance 传递给 。或者也许有理由这样做,但我看不出来。operator ==std::unordered_multimapPredstd::is_permutation

0赞 Ranoiaetep 11/14/2022 #2

提供自己的只会在内部更改行为,即插入新项目。但是,它对 没有影响。KeyEqualoperator==

根据 operator==std::unordered_multiset),它的行为就好像每个等价的 s 都与 进行了比较。equal_rangestd::is_permutation

您可以潜在地针对 C++20 之前设置的行为进行专门化(这是自 C++20 以来未定义的行为):std::is_permutation

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==