修改c++ std::set中元素中与排序相关的部分

Modify sort-relevant parts of an element in a C++ std::set

本文关键字:排序 元素 c++ std set 修改      更新时间:2023-10-16

我知道这是有问题的,但我很好奇这样做是否会导致未定义的行为?

class A
{
public:
    int i;
};
class As {
public:
    bool operator()(const A* l, const A* r)
    {
        return l->i < r->i;
    }
};
int main() {
    std::set<A*,As> s;
    A* a1 = new A();
    a1->i = 9;
    A* a2 = new A();
    a2->i = 2;
    s.insert(a1);
    s.insert(a2);
    for(std::set<A*,As>::iterator i = s.begin(); i != s.end(); ++i)
        std::cout<<(*i)->i<<std::endl;
    for(std::set<A*,As>::iterator i = s.begin(); i != s.end(); ++i)
    {
        if((*i)->i == 2)
            (*i)->i = 22;
    }
    for(std::set<A*,As>::iterator i = s.begin(); i != s.end(); ++i)
        std::cout<<(*i)->i<<std::endl;
    //now s is in incorrect state, insert all elements into a tmp one
    std::set<A*,As> t;
    for(std::set<A*,As>::iterator i = s.begin(); i != s.end(); ++i)
        t.insert(*i);
    s.swap(t);
    for(std::set<A*,As>::iterator i = s.begin(); i != s.end(); ++i)
        std::cout<<(*i)->i<<std::endl;
}

输出为:

2
9
22
9
9
22

在GCC 4.7编译时似乎工作得很好,但我不知道这是否只是运气。

标准(c++ 11)说明了在关联容器中使用的键比较函数,即std::set:

(§23.2.4/3)[…对于同一容器中的任意两个键k1k2,调用comp(k1,k2)将始终返回相同的值。

这里的comp指的是比较函数,在你的例子中是函子As()

显然,当键包含指向外部对象的指针,并且调用comp(k1,k2)的结果依赖于这些外部对象时,则修改这些对象会导致违反上述规则。

标准没有说如果你违反了这个规则会发生什么。所以,正式地说,你有未定义的行为(因为标准没有定义行为)。

在实践中,我猜有一个很大的机会,可怕的事情,包括分割错误,可能会发生作为违反的结果,因为下次搜索集的关键字,或插入,搜索算法& & &;建立在键按顺序排列的假设之上& & &;可能会误入歧途,进行越界访问等。

所以结论是,是的,这是UB,即使它在实践中只在少数选定的情况下有效,也可能导致不好的事情发生。