在集合中失去严格的弱排序

时间:2011-01-07 22:58:09

标签: c++ stl iterator set

我认为STL容器集和map以严格的弱顺序提供元素。但是,我发现如果通过解除引用获取迭代器并通过解除引用更改元素的值,则它不会恢复顺序,这违反了23.1.2.2和23.3.3.2。这是代码

    int nv = 3;
set<int> s = set<int>();
s.insert(5);
s.insert(10);
s.insert(20);
s.insert(30);
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit)
    cout<<*cit<<" ";
cout <<endl;
set<int>::iterator it = s.find(10);
*it = nv;
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit)
    cout<<*cit<<" ";
cout <<endl;
s.insert(40);
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit)
    cout<<*cit<<" ";
cout <<endl;

产生

5 10 20 30
5 3 20 30
5 3 20 30 40

这是我的STL版本(MS VS 2008)中的错误吗?或者我错了吗?

6 个答案:

答案 0 :(得分:4)

std::mapstd::set仅对插入执行排序。如果您更改现有项目的键/值,则不会发生任何魔法,结果可能未定义。

要获得所需效果,您必须删除原始元素,然后插入一个新元素。

答案 1 :(得分:4)

set的iterator和const_iterator都是常量迭代器。您不能修改set的值,只能删除和插入。

答案 2 :(得分:2)

看起来像那个版本的STL中的错误。

在我的g ++上,我收到以下错误:
t2.cpp:18:8: error: assignment of read-only location ‘it.std::_Rb_tree_const_iterator<_Tp>::operator* [with _Tp = int, const _Tp& = const int&]()’

答案 3 :(得分:1)

通常情况下,您无法通过迭代器修改关联容器元素的键部分。代码根本不会编译。

如果您的标准库实现允许*it = nv赋值,那么它必须是该实现的一个怪癖。 AFAIK,允许编译此作业并非严格违法。这更像是一个实施质量问题。

分配不会使用Comeau实现进行编译。 MS的实施明显不那么严格。

答案 4 :(得分:1)

更改std::set中的元素可能违反内部排序并破坏您的数据结构。只有一些STL实现通过禁止修改iterator指向的内容来防止这种情况,因此您必须自己强制执行该约束。

这就像通过调用std::vector(以腾出空间)并使用指针算法来编写reserve的逻辑结尾 - 你可以这样做,但它会破坏你的std::vector

如果要更改集合中的元素,这应该有效:

std::set<int> my_set;

// initialize my_set ...

std::set<int>::iterator itr = my_set.find(10);
if (itr != my_set.end()) {
    my_set.erase(itr++); // ++ avoids invalidating iterator
    my_set.insert(3);
}

如果您想继续使用它,您只需要增加itr

(来源:有效STL ,“第22项,”Scott Meyers)

答案 5 :(得分:0)

std::mapstd::set提供 严格的弱订购,他们需要一个。您必须提供排序(默认值为std::less,适用于int)。

如果您更改了setmap元素的排序方式,那么由于排序不稳定,您违反了要求。