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

C++ 修改排序C+中元素的相关部分+;std::set,c++,set,std,C++,Set,Std,我知道这是有问题的,但我很好奇这样做是否会导致未定义的行为 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->

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

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;
}
用GCC4.7编译时,它似乎工作得很好,但我不知道这是否只是运气好。

标准(C++11)提到了关联容器中使用的键比较函数,如
std::set

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

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

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

标准没有说明如果你违反这条规则会发生什么。因此,从形式上讲,您有未定义的行为(因为标准没有定义行为)

在实践中,我想很有可能因为违规而发生可怕的事情,包括分段错误,因为下次在集合中搜索密钥或进行插入时,搜索算法——建立在密钥有序的假设之上——可能会误入歧途,进行越界访问等


因此,结论是,是的,这是UB,即使它在实践中的一些选定案例中起作用,也可能导致不好的事情发生。

这几乎肯定是未定义的行为。您已经更改了集合元素的排序条件。是的,我知道这很糟糕,但是,仅从示例来看,这似乎是可以接受的。“断集”上唯一的操作是++迭代器,它是在最后一次插入操作之后确定的。如果它是未定义的行为,那么您不能依赖任何工作。
2
9
22
9
9
22