I'm working on a school project of boolean minimization, and here I want to delete some elements of a set of my user defined class. This is where the error occurs:
(dc and PI are both sets of my class Term
, passed to this function by reference. std::set<Term>& dc, PI
)
for (const auto& n : dc) {
for (const auto& i : n.getMinterm()) {
m[i] = 0;
for (auto &x : PI) {
x.delMinterm(i);
}
}
}
The error message is:
- Error (active) E1086 the object has type qualifiers that are not compatible with the member function "Term::delMinterm"
- Error C2662 'void Term::delMinterm(int)': cannot convert 'this' pointer from 'const Term' to 'Term &'
This is the content of my class:
class Term {
private:
int group = 0;
int literal = 0;
std::string term;
std::set<int>minterm;
bool isDontCare;
bool merged;
};
Function delMintern(int)
just erases the selected element from the set minterm
.
Though I didn't use "const auto&" but "auto&", it still shown as a const object.
I've tried taking off the '&' but it just create a local duplicate, however, I want to directly modify the original one.
I also tried something like:
for (auto x : PI) {
PI.erase(x);
x.delMinterm(i);
PI.insert(x);
}
but it caused a "read access violation" error.
CodePudding user response:
You can't modify a reference to x
because it is const
. It is const because iterating a std::set
through loop gives only const values.
See solution with const_cast
example code at the end of my answer.
It is known that std::set
stores all entries in a sorted tree.
Now imagine if you can modify a variable when iterating a loop, it means that after modification sorted order of std::set
might be changed. But std::set
should always keep invariant of its tree, hence it can't allow to make any modifications thus gives only const
values when iterating.
If you need to really modify set entry then you have to take it from set, delete from set and that add again to set. Even if sorted order is not changed after your modification, still you need to reinsert into set.
But there is a hacky workaround - you can make your method delMinentry
as having const
modifier. Then all fields that it modifies should be marked as mutable
. Mutable fields allow modifications from const
methods. And std::set
allows to call const
methods when iterating.
There is one more workaround - you make delMinterm()
as const
method, but inside this method do const_cast<Term &>(*this).delMintermNonConst()
, in other words from const method you can call non-const method if you do const_cast
. Also you can do const cast directly on loop variable if you're sure what you do, in other words if you modify Term in such a way that std::set
sorted order is not changed:
for (auto &x : PI) {
const_cast<Term &>(x).delMinterm(i);
}
If delMinterm()
results in such modification of a Term after which order of std::set
may change then you can't do const_cast
in code above. In other words if after delMinterm your operator <
may give a different result between terms, then you can't do this const cast and you have to reinsert into set (delete Term and add again).
Also don't forget that after reinserting into set you have to redo set iteration loop again from start, because after change to inner structure you can't keep iterating loop running further, iterators are invalidated.
If set's order changes (hence you can't do const_cast
) then you have to re-insert values of set, do this by copying values to vector, modifying them through delMinterm(), copying back to set, like this:
std::vector<Term> scopy(PI.cbegin(), PI.cend());
for (auto & x: scopy)
x.delMinterm(i);
PI = std::set<Term>(scopy.cbegin(), scopy.cend());