错误:没有匹配的成员函数来调用' push_back'

时间:2018-05-14 03:38:59

标签: c++ vector struct set

为什么我在最后两行收到错误?目标是在集合中查找对象,并修改其内容。

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);
                                   ^

2 个答案:

答案 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。由于在一个集合中不允许重复,因此,实际上,如果ab之前,则为a < b。如果要违反此不变量,则任何需要对其进行排序的设置操作(例如findinsert)都将具有意外(确切地说,未定义)行为。

因此,std::set不允许您使用iterator更改项目,因为您可能会无意中更改项目的成员,从而影响其在集合中的正确位置,从而打破不变,从而导致不确定的行为。

不幸的是,编译器不够聪明,不能理解你的比较函数只评估某些成员,在这种情况下只评估id。如果编译器和语言能够分析和表达这一点,他们可能会认为虽然i->id应该是const的引用,但i->m对于任何其他成员m都可以安全地对非const的引用。

您的问题至少有四种可能的解决方案:

  1. 最简单但最丑陋的解决方案是将您需要修改的成员标记为mutable。请注意,您必须自己确保更改它们不会影响排序顺序。
  2. 另一个相当丑陋的解决方案是在const_cast中故意使用const_cast<mystruct &>(*i),其中i是迭代器。同样,永远不要以这种方式更改对排序顺序产生影响的成员。
  3. 然而,更优雅的解决方案是额外的运行时间开销,是将指针间接级别(例如,使用std::unique_ptr)添加到要修改的属性。但是请注意,如果你在比较函数中使用 pointee ,你仍然会冒破坏集合不变量的风险,只有现在编译器和库将不再阻止你这样做!
  4. 即使如果你想要更改影响排序顺序的成员,唯一有效的方法是制作项目的副本,修改副本,删除旧项目,然后重新插入复制。这允许容器在必要时将副本插入不同的位置。
  5. 最后的说明:

    • 诸如std::unordered_set之类的散列容器将具有完全相同的问题,仅在这种情况下它不是比较函数,而是您必须考虑的散列和相等函数。
    • 由于给定的原因,std::mapstd::unordered_map可能更适合您的问题域,因为在这种情况下,库知道mapped_type不习惯确定容器结构。另请注意,value_typestd::map的{​​{1}}如何std::unordered_map而不是std::pair<const key_type, mapped_type>,原因与更改密钥可能会破坏容器不变量的原因相同。