通过迭代器更改类成员

4
我正在学习C++,但是无法理解这个问题:
我有一个简单的A类。
class A {
private:
    int ival;
    float fval;

public:
    A(int i = 0, float f = 0.0) : ival(i), fval(f) { }
    ~A(){ }
    void show() const { 
        cout << ival << " : " << fval << "\n";
    }
    void setVal(int i) {
        ival = i;
    }

    //const getters for both ival and fval

    //used for the default "lesser"
    friend bool operator<(const A& val1, const A& val2) {
        return val1.ival < val2.ival ? true : false;;
    }
}

然后我有一个常规的 set<A> myset,在循环中使用 insert(A(2, 2.2)); 填充。
遍历以获取所有值并不是问题,但我想在这次迭代中修改值:
for(set<A>::iterator iter = set3.begin(); iter != set3.end(); iter++) {
    iter->setVal(1);
}

我认为这应该是可行的,就像您在Java中在foreach循环中所做的那样。编译时出现错误:error: passing ‘const A’ as ‘this’ argument of ‘void A::setVal(int)’ discards qualifiers
查看STL set的源代码,我发现begin()仅作为const方法可用,我认为这可能是问题所在。尝试在setVal()方法上使用const会得到相同的错误,并且没有多大意义,因为我想修改A的值。
这是用循环改变一堆A值的错误方法吗?

+1 是因为你似乎先自己搜索了一下,当你学习新东西时这总是一个好习惯。 - ereOn
请注意,val1.ival < val2.ival ? true : falseval1.ival < val2.ival 是相同的。 - Pedro d'Aquino
@Pedro:谢谢,你这么说现在很明显,但我自己没看出来。 - DrColossos
2个回答

5
STL set不允许更改其存储的值。它通过迭代器(而不是集合中实际的对象)返回对象的副本来实现这一点。
set这样做的原因是因为它使用<来对集合进行排序,如果每次解引用迭代器都要重新制作整个树,它将不得不这样做,因为它不知道您是否更改了任何更改排序的内容。
如果需要更新set<>,请删除旧值并添加新值。
编辑:刚刚检查了SGI STL的源代码,发现如下内容:
 typedef typename _Rep_type::const_iterator iterator;

因此,set::iterator仅仅是set::const_iterator。


这是否意味着我只是尝试了一个集合而不是另一个容器,所以运气不好?这对向量、列表等容器也适用吗? - DrColossos
1
你应该使用const&,而不是拷贝。 如果是拷贝的话,它就不会是const了。 - Scharron
@DrColossos 它可以与任何非有序容器(列表、向量、无序集合等)一起使用。 - Scharron
我曾经使用过实际上进行复制的set<>实现,因此强制转换常量并不能帮助你。你必须假设你无法通过强制转换来解决这个问题。 - Lou Franco
如果一个实现返回了键的副本,那么它将不符合规范。另外请注意,您不能必须不尝试使用const-cast修改对象,因为这可能会破坏集合的不变性。这在SO上已经被问过很多次了。 - David Rodríguez - dribeas

1

this page来看,似乎存在begin()非const方法。

也许您的集合被作为const引用传递给了该方法?

编辑

参考页面是错误的。正如Scharron所述,有序容器中不存在非const的begin()(或者end())方法。

我会告知网站他们的错误(这不是他们第一次犯错;))


不会吧(至少我认为不会?)。我使用 set<A> myset;,然后像我上面描述的那样添加 A - DrColossos
我知道这个页面,是一个很好的资源!但是如果源代码中的定义被标记为const,那么非const的begin()从哪里来? - DrColossos
这是一个错误。 在有序容器中,iterator 和 const_iterator 是相同的类型(因此只有一个 begin() constend() const 方法)。 - Scharron
@DrColossos和@Scharron:看起来确实是一个错误。我刚试了一下,没有非const的begin()方法。或者至少,我的编译器不知道有这样的方法。 - ereOn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接