为什么我在最后两行收到错误?目标是在集合中查找对象,并修改其内容。
using namespace std;
struct mystruct {
int id;
vector<int> y;
mystruct(const int id):id(id) {}
bool operator<(const mystruct& x) const { return id < x.id; }
bool operator==(const mystruct& x) const { return id == x.id; }
};
void test() {
std::set<mystruct> sx;
mystruct x(1);
x.y.push_back(1); x.y.push_back(2);
sx.insert(x);
//
set<mystruct>::iterator i = sx.find(1);
const mystruct* x1 = &(*i);
const mystruct x2 = *x1;
cout << &(i->y) << endl;
cout << &(x1->y) << endl;
cout << x2.id << endl;
x2.y.push_back(3);
i->y.push_back(4);
}
似乎迭代器返回一个常量对象,并且不允许我使用push_back()
来修改向量y
。我怎么能克服这个?
错误:
test.cpp:27:8: error: no matching member function for call to 'push_back'
x2.y.push_back(z);
~~~~~^~~~~~~~~
/opt/local/libexec/llvm-6.0/include/c++/v1/vector:688:36: note: candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
_LIBCPP_INLINE_VISIBILITY void push_back(const_reference __x);
^
/opt/local/libexec/llvm-6.0/include/c++/v1/vector:691:36: note: candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
_LIBCPP_INLINE_VISIBILITY void push_back(value_type&& __x);
^
答案 0 :(得分:4)
由于x2
使用const
限定符声明,const mystruct x2
,C ++编译器仅考虑const
- x2
上所有调用的合格成员函数任何成员。特别是,它正在寻找要调用的void push_back (const int& val) const
成员函数。显然,没有这样的函数,因为push_back
必须修改容器,因此编译器会产生错误,准确解释发生了什么:
候选函数不可行:
'this'
参数的类型为'const vector<int>'
,但方法未标记为const
在代码中解决此问题的唯一方法是从const
的声明中删除x2
限定符。
答案 1 :(得分:3)
您无法修改x2
的原因是它被声明为const
,正如@dasblinkenlight所指出的那样。 @ songyuanyao的注释是正确的,因为访问迭代器引用的对象,但没有完全回答这个问题,因为它没有说为什么设置迭代器只允许const
访问。
原因是,正如您所知,std::set
是一个有序容器,其结构是通过使用(默认情况下)operator <
比较条目来确定的。这意味着有一个容器不变,如果项a
位于b
中的另一个项std::set
之前,则会跟随!(b < a)
。我这样说是因为这也适用于std::multiset
。由于在一个集合中不允许重复,因此,实际上,如果a
在b
之前,则为a < b
。如果要违反此不变量,则任何需要对其进行排序的设置操作(例如find
或insert
)都将具有意外(确切地说,未定义)行为。
因此,std::set
不允许您使用iterator
更改项目,因为您可能会无意中更改项目的成员,从而影响其在集合中的正确位置,从而打破不变,从而导致不确定的行为。
不幸的是,编译器不够聪明,不能理解你的比较函数只评估某些成员,在这种情况下只评估id
。如果编译器和语言能够分析和表达这一点,他们可能会认为虽然i->id
应该是const
的引用,但i->m
对于任何其他成员m
都可以安全地对非const
的引用。
您的问题至少有四种可能的解决方案:
mutable
。请注意,您必须自己确保更改它们不会影响排序顺序。const_cast
中故意使用const_cast<mystruct &>(*i)
,其中i
是迭代器。同样,永远不要以这种方式更改对排序顺序产生影响的成员。std::unique_ptr
)添加到要修改的属性。但是请注意,如果你在比较函数中使用 pointee ,你仍然会冒破坏集合不变量的风险,只有现在编译器和库将不再阻止你这样做!最后的说明:
std::unordered_set
之类的散列容器将具有完全相同的问题,仅在这种情况下它不是比较函数,而是您必须考虑的散列和相等函数。std::map
或std::unordered_map
可能更适合您的问题域,因为在这种情况下,库知道mapped_type
不习惯确定容器结构。另请注意,value_type
或std::map
的{{1}}如何std::unordered_map
而不是std::pair<const key_type, mapped_type>
,原因与更改密钥可能会破坏容器不变量的原因相同。