无法修改基于范围的循环中引用的值

Can't modify the value of a reference in Range based loop

我正在做一个布尔最小化的学校项目,在这里我想删除我的用户定义的一组元素的一些元素 class。 这是错误发生的地方:

(dc 和 PI 都是我的 class Term 的集合,通过引用传递给这个函数。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);
            }
        }
    }

错误信息是:

  1. 错误(活动)E1086 对象具有与成员函数“Term::delMinterm”不兼容的类型限定符
  2. 错误 C2662 'void Term::delMinterm(int)': 无法将 'this' 指针从 'const Term' 转换为 'Term &'

这是我class的内容:

class Term {

private:
    int group = 0;
    int literal = 0;
    std::string term;
    std::set<int>minterm;
    bool isDontCare;
    bool merged;
};

函数 delMintern(int) 只是从集合 minterm 中删除选定的元素。

Though I didn't use "const auto&" but "auto&", it still shown as a const object.

我试过去掉'&'但是它只是创建了一个本地副本,但是,我想直接修改原来的。

我也试过类似的东西:

    for (auto x : PI) {
        PI.erase(x);
        x.delMinterm(i);
        PI.insert(x);
    }

但它导致了“读取访问冲突”错误。

您不能修改对 x 的引用,因为它是 const。它是常量,因为通过循环迭代 std::set 只给出常量值。

在我的答案末尾查看带有 const_cast 示例代码的解决方案。

已知 std::set 将所有条目存储在排序树中。

现在想象一下,如果你可以在循环迭代时修改一个变量,这意味着修改后 std::set 的排序顺序可能会改变。但是 std::set 应该始终保持其树的不变性,因此它不允许进行任何修改,因此在迭代时只给出 const 值。

如果你真的需要修改集合条目,那么你必须从集合中取出它,从集合中删除,然后再次添加到集合中。即使你修改后排序没有改变,你仍然需要重新插入集合。

但是有一个 hacky 解决方法 - 您可以使您的方法 delMinentry 具有 const 修饰符。然后它修改的所有字段都应标记为 mutable。可变字段允许从 const 方法进行修改。 std::set 允许在迭代时调用 const 方法。

还有一个变通方法——你将 delMinterm() 作为 const 方法,但在这个方法中做 const_cast<Term &>(*this).delMintermNonConst(),换句话说,你可以从 const 方法调用非 const 方法如果你这样做 const_cast。如果您确定要做什么,也可以直接在循环变量上执行 const 转换,换句话说,如果您以 std::set 排序顺序不变的方式修改 Term:

for (auto &x : PI) {
    const_cast<Term &>(x).delMinterm(i);
}

如果 delMinterm() 导致对 Term 的这种修改,之后 std::set 的顺序可能会改变,那么您不能在上面的代码中执行 const_cast。换句话说,如果在 delMinterm 之后你的 operator < 可能会在术语之间给出不同的结果,那么你不能做这个 const 转换,你必须重新插入到集合中(删除 Term 并再次添加)。

另外不要忘记,在重新插入 set 后,您必须从头开始重新执行 set 迭代循环,因为更改内部结构后,您无法继续迭代循环 运行,迭代器将失效。

如果 set 的顺序改变(因此你不能做 const_cast)那么你必须重新插入 set 的值,通过将值复制到 vector,通过 delMinterm() 修改它们,复制回来设置,像这样:

std::vector<Term> scopy(PI.cbegin(), PI.cend());
for (auto & x: scopy)
    x.delMinterm(i);
PI = std::set<Term>(scopy.cbegin(), scopy.cend());