修改std :: set的元素会发生什么?

rlb*_*ond 12 c++ collections stl set

如果我通过迭代器更改std :: set的元素,我知道它不是"重新插入"或"求助",但有没有提到它是否触发了未定义的行为?例如,我认为插入会搞砸.有没有具体提到会发生什么?

180*_*ION 24

您不应直接编辑存储在集合中的值.我从MSDN文档中复制了这个,这有点权威:

STL容器类集用于存储和检索集合中的数据,其中包含的元素的值是唯一的,并且用作根据其自动排序数据的键值.集合中元素的值可能不会直接更改.相反,您必须删除旧值并使用新值插入元素.

为什么这很容易理解.该set实施将无法知道你已经修改了其背后的价值的方式.正常实现是红黑树.更改了值后,该实例树中的位置将是错误的.您可能希望看到各种错误行为,例如由于exists搜索沿着树的错误分支返回错误结果.

  • "设置实现将无法知道你已经修改了它背后的值"实际上,正如下面的coppro提到的那样,你不能通过常规方法修改它背后的值,因为它的迭代器是const.这就是他们所说的"可能不会直接改变". (3认同)
  • 我想象一个包含shared_ptr的集合到我的底层对象 - 然后你通过迭代器中的引用调用底层对象到一个修改用于排序的可变成员的const函数.我认为,当他们说"可能不会直接改变"时,他们的意思是"不要做这种疯狂的事情",而不是"不可能修改成员" (2认同)

小智 5

确切的答案取决于平台,但作为一般规则,"键"(放在集合中的东西或第一种类型的映射)被认为是"不可变的".简而言之,不应修改它,也不要自动重新插入.

更准确地说,不能修改用于比较密钥的成员变量.

Windows vc编译器非常灵活(使用VC8测试)并且此代码编译:

// creation
std::set<int> toto;
toto.insert(4);
toto.insert(40);
toto.insert(25);

// bad modif
(*toto.begin())=100;

// output
for(std::set<int>::iterator it = toto.begin(); it != toto.end(); ++it)
{
    std::cout<<*it<<" ";
}
std::cout<<std::endl;
Run Code Online (Sandbox Code Playgroud)

输出是100 25 40,显然没有排序......糟糕......但是,当你想修改不参与运算符的数据时,这种行为很有用.但是你最好知道你在做什么:这是你过于灵活的价格.

有些人可能更喜欢gcc行为(使用3.4.4测试),这会产生错误"只读位置的分配".您可以使用const_cast解决它:

const_cast<int&>(*toto.begin())=100;
Run Code Online (Sandbox Code Playgroud)

现在也在gcc上进行编译,输出相同:100 25 40.但至少,这样做可能会让你想知道发生了什么,然后去堆栈溢出并看到这个线程:-)