-2

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:

  1. Error (active) E1086 the object has type qualifiers that are not compatible with the member function "Term::delMinterm"
  2. 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.

user438383
  • 5,716
  • 8
  • 28
  • 43
hmlssslmn
  • 11
  • 2
  • Can you show how `PI` variable is declared? Looks like it is const and hence gives const reference to `x`. – Arty Nov 14 '21 at 10:31
  • Please read the [tour] and check the [help], to inform yourself what and how you can ask here. You're required to post a [mcve] to get help with your problem. – πάντα ῥεῖ Nov 14 '21 at 10:32
  • 1
    Even if you write `auto& x`, nothing prevents the compiler from choosing to "replace" `auto` with `Type const`, should lhs have type `Type const`... You get similar behaviour with templates btw: `template void PrintAll(T& list) { for (auto& element : list) { std::cout << element << '\n'; } }` nothing prevents you from passing a `std::vector const` to the function even though `list` is not declared as reference to `T const` – fabian Nov 14 '21 at 10:33
  • If you write `for (Term &x : PI)` instead of `for (auto &x : PI)` does it fix your problem? If it does then looks like some trouble with compiler, because it should substitute `Term &` in place of `auto &` if possible and if not possible only then `Term const &`. – Arty Nov 14 '21 at 10:35
  • @Arty I tried `for (Term &x : PI)` and got this warning on PI: `C++ qualifiers dropped in binding reference of type to initializer of type` – hmlssslmn Nov 14 '21 at 10:43
  • @hmlssslmn Can you send here a piece of code where `PI` variable is declared/defined? – Arty Nov 14 '21 at 10:47
  • So again most likely `PI` is const (or yields const elements when iterated over) - please show `PI` – CherryDT Nov 14 '21 at 10:49
  • @Arty The original declaration is `std::set PI`, and I pass it to functions by reference `std::set& PI` – hmlssslmn Nov 14 '21 at 10:50
  • 1
    @hmlssslmn Please, put a look at [my answer](https://stackoverflow.com/a/69962321/941531), I just posted it now. That happens because `std::set` doesn't allow modifications when iterating hence gives only `const` entries on iteration. – Arty Nov 14 '21 at 11:02
  • @hmlssslmn Please read very end of my answer again, I just added 2 paragraphs, right after `const_cast` example. They say that you can't do const cast if `delMinterm()` may change an order of set. You have to delete element from set and then add it again to set, so that set re-sorts its elements after `delMinterm` modification. It may not apply to you and you still can do `const_cast` in a case if `delMinterm()` doesn't change order of a set. – Arty Nov 14 '21 at 11:12
  • @Arty Got it, I've tried your method and it works. Thanks for you helping :) – hmlssslmn Nov 14 '21 at 11:14
  • @hmlssslmn I just put a look at your full code. And I see that `delMinterm()` modifies inner field `minterm`. Also I see that `operator <` in your `Term` compares two minterms as `return minterm < rhs.minterm;`. It means that `delMinterm()` will influence result of `<` comparison between two Terms. It means that order of `std::set` will change after you do `delMinterm()`. Hence you can't do `const_cast` solution that I gave you.Because you modify entry of `std::set` while order of set is not updated.**Important** you have to delete Term from set and insert again to set instead const cast – Arty Nov 14 '21 at 11:21
  • @Arty I changed the comparing method of `operator >` and `operator <`, so that the order of the set won't change after `delMinterm()`,then I can do `const_cast` ! – hmlssslmn Nov 14 '21 at 11:26
  • @hmlssslmn Yes, if order is not changed you can do `const_cast`. But if it is changed you still solve your task, just instead of modifying set elements in-place just copy element, delete it from set, modify element, add it to set again. In other words do `std::vector scopy(PI.cbegin(), PI.cend()); for (auto & x: scopy) x.delMinterm(i); PI = std::set(copy.cbegin(), copy.cend());`. – Arty Nov 14 '21 at 11:30
  • @hmlssslmn Just updated my answer again, added at the end example of code that re-inserts all elements of set with modifying it through `delMinterm()`. – Arty Nov 14 '21 at 11:34
  • @hmlssslmn BTW, I see you use variable name `PI`. Just interesting, what project are you doing? Maybe you're computing digits of `PI` number? Because surprisingly I'm computing PI right now too through my special algorithm/method. – Arty Nov 14 '21 at 11:37
  • @Arty I'm doing a boolean minimization project with Quine-McClusky algorithm and Petrick's method from my digital logic course (I'm a college freshman), my `PI` stands for `Prime Implicant` ! – hmlssslmn Nov 14 '21 at 11:56

1 Answers1

1

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());
Arty
  • 14,883
  • 6
  • 36
  • 69