如果我通过迭代器更改std :: set的元素,我知道它不是“重新插入”或“使用”,但有没有提到它是否触发了未定义的行为?例如,我认为插入会搞砸。有没有具体提到会发生什么?
答案 0 :(得分:24)
您不应直接编辑存储在集合中的值。我从MSDN文档中复制了这一点,这有点权威:
使用STL容器类集 用于存储和检索数据 从一个集合中的价值观 所包含的元素是独一无二的 并作为关键值 数据是自动的 订购。 a中元素的值 设置可能无法直接更改。 相反,您必须删除旧值 并使用新值插入元素。
为什么这很容易理解。 set
实现无法知道您已修改其背后的值。正常实现是红黑树。更改了值后,该实例树中的位置将是错误的。您可能希望看到各种错误的行为,例如exists
查询返回错误的结果,因为搜索沿着树的错误分支返回。
答案 1 :(得分: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;
输出 100 25 40 ,显然没有排序......糟糕... 当您想要修改未参与运算符&lt; 的数据时,此类行为仍然很有用。但你最好知道自己在做什么:这就是因为过于灵活而得到的代价。
有些人可能更喜欢gcc行为(使用3.4.4测试),这会产生错误“只读位置的分配”。你可以使用const_cast解决它:
const_cast<int&>(*toto.begin())=100;
现在正在编译gcc,输出相同: 100 25 40 。 但至少,这样做可能会让你想知道发生了什么,然后去堆栈溢出并看到这个线程: - )
答案 2 :(得分:4)
你不能这样做;他们是const
。 set
没有方法可以检测到您对内部元素进行了更改,因此您无法执行此操作。相反,您必须删除并重新插入元素。如果使用复制昂贵的元素,则可能必须切换到使用指针和自定义比较器(或切换到支持rvalue引用的C ++ 1x编译器,这会使事情变得更好)。