Why am I getting the error at the last two lines? The goal is to find the object in a set, and modify its content.
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);
}
It seems like the iterator returns a constant object, and doesn't let me use push_back()
to modify the vector y
. How can I overcome this?
Error:
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);
^
No matching function for call means when we are calling some certain functions, but the id of that function is not matching the argument of the function that is defined. Hence we obtain an error 'no matching function for a call' to C++.
push_back method() in C++ is a method that is part of the vector as a data structure in C++. It is used for pushing elements from the back of the vector.
vector::push_back() push_back() function is used to push elements into a vector from the back. The new value is inserted into the vector at the end, after the current last element and the container size is increased by 1.
Since x2
is declared with a const
qualifier, i.e. const mystruct x2
, C++ compiler considers only const
-qualified member functions for all invocations on x2
and any of its members. In particular, it is looking for void push_back (const int& val) const
member function to invoke. Obviously, there is no such function, because push_back
must modify the container, so the compiler produces an error explaining exactly what's going on:
candidate function not viable:
'this'
argument has type'const vector<int>'
, but method is not markedconst
The only way to address this in your code is to remove const
qualifier from x2
's declaration.
The reason why you cannot modify x2
is that it is declared const
, as has been pointed out by @dasblinkenlight. @songyuanyao's comment is right as for accessed to the object referenced by the iterator, but doesn't quite answer the question because it does not say why set iterators only allow const
access.
The reason for this is that, as you know, a std::set
is an ordered container whose structure is determined by comparing entries to one another using (by default) operator <
. This means that there is a container invariant such that if an item a
precedes another item b
in the std::set
, it follows that !(b < a)
. I put it like this because this also holds for std::multiset
. Since, in a set, duplicates are not allowed, it follows that, actually, if a
precedes b
, then a < b
. If this invariant were to be violated, then any set operation that requires the set to be sorted, such as find
or insert
, will have unexpected (to be precise, undefined) behavior.
Therefore, a std::set
will not allow you to change an item using an iterator
, because you could unwittingly change members of the item in a way that affect its proper place in the set, breaking the invariant and thereby causing undefined behavior.
Unfortunately, the compiler is not smart enough to understand that your comparison function only evaluates certain members, in this case, only id
. If compiler and language were capable of analyzing and expressing this, they might figure that while i->id
should be a reference to const
, i->m
for any other member m
could safely be a reference to non-const
.
There are at least four possible solutions to your problem:
mutable
. Note that you must yourself ensure that changing them does not affect the sort order.const_cast
as in const_cast<mystruct &>(*i)
where i
is an iterator. Again, never change a member that has an effect on the sort order in this way.std::unique_ptr
) to properties that you want to modify. Note, however, that if you were to use the pointee in your comparison function, you would still run the risk of breaking the set invariant, only now the compiler and library will no longer prevent you from doing so!Final notes:
std::unordered_set
will have exactly the same issue, only in this case it is not the comparison function, but both the hash and equality functions that you have to consider.std::map
or std::unordered_map
may be a better match for your problem domain, as, in this case, the library knows that the mapped_type
is not used to determine container structure. Note also how the value_type
of a std::map
or std::unordered_map
is std::pair<const key_type, mapped_type>
instead of std::pair<key_type, mapped_type>
exactly for the same reason that changing the key could break the container invariants.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With