【问题标题】:const references of set elements do not preserve information集合元素的 const 引用不保留信息
【发布时间】:2020-04-20 08:01:39
【问题描述】:

我有两个朋友班:

class Node {
private:
    unsigned id_;
    bool marked_;
    std::set<Node> neighbors_;

    friend class Graph;
    ......

public:
bool operator == (const Node & other) const { return id_ == other.id(); }
bool operator < (const Node & other) const { return id_ < other.id(); }
......
};

class Graph {
private:
    std::set<Node> vertices_{};
    void reach_set_helper(unsigned id, std::vector<unsigned> &reach_set);
    ......
};

我正在尝试创建一个函数,该函数可以首先在图形vertices_ 中找到特定节点,例如节点v。然后我想更改v 的邻居的marked_ 属性。要找到v,我必须使用std::find() 方法。但是,此方法的返回迭代器不允许我更改邻居的成员变量。这是我尝试过的:

Node & s = v;
std::set<Node>::iterator pos = vertices_.find(s);
const std::set<Node> & neighbors = (pos->neighbors_);
for (std::set<Node>::iterator it = neighbors.begin(); it != neighbors.end(); it++) {
    if (!it->is_marked()) {
        reach_set.push_back(it->id());
        it->set_marked(true);
        this->reach_set_helper(it->id(), reach_set);
    }
}

请注意,我必须使用const std::set&lt;Node&gt; &amp; neighbors,因为我想原地更换邻居。但是我不能通过const迭代器it改变v的邻居,所以这个方法行不通。我有一种方法可以通过擦除来更改vertices_,然后通过迭代器复制回来,这不是问题。但这里不一样,我是对顶点的元素做操作,这是另一个集合。有什么建议吗?

更新

根据@walnut 的建议,我刚刚将marked_ 更改为 mutable,现在我可以编写以下代码

std::set<Node>::iterator pos = vertices_.find(s);
const std::set<Node> & neighbors = (pos->neighbors_);
for (const Node & nbr : neighbors) {
    if (!nbr.is_marked()) {
        reach_set.push_back(nbr.id());
        Stack.push(nbr);
        nbr.marked_ = true;
    }
}

但是,这并没有解决问题,因为上面代码中的nbr现在丢失了自己的neighbors的信息,我仍然无法使用它来遍历图形。

这很奇怪,因为nbr 是原始图节点的引用?例如,我什至不能用它来打印我的图表!

void MtxGraph::print_graph() const {
    const Node & start = *vertices_.begin();
    print_graph_helper(start);
}

void MtxGraph::print_graph_helper(const Node & v) const {
    std::set<Node> neighbors = v.neighbors_;
    for (const Node & node : neighbors) {
        std::cout << v.id() << " => " << node.id() << " => ";
        print_graph_helper(node);
        std::cout << std::endl;
    }
}

上面的代码也不起作用,因为引用 node 不保留它所引用的对象的邻居信息。

【问题讨论】:

  • 我正在使用std::set&lt;Node&gt;。我的operator== 由他们的id 号码定义。如果id 相同,则它们相等。哦,我没有可用的 C++ 17,现在对我来说似乎太花哨了。只是想知道在当前版本中是否有办法解决这个问题。
  • 感谢@walnut,operator&lt; 也是使用他们的id 定义的,较小的id 具有较小的价值。
  • 在这种情况下,这是否回答了您的问题? (std::setstd::map 的行为方式相同)Modify key of std::mapHow to update an existing element of std::set?C++ std::set update is tedious: I can't change an element in place。另见stackoverflow.com/questions/6068167
  • 您是否建议将Nodestd::set 更改为std::map?如果是这样,我将如何在节点中拥有marked 属性?
  • 不一定。我建议在链接问题的答案中使用其中一种方法(它们以相同的方式适用于std::setstd::map):要么使marked_mutable删除/修改/插入Node std::map&lt;NodeKey, NodeValue&gt; (在C ++ 17中)使用@987654363将类分为键相关和键无关状态@.

标签: c++ class c++11 iterator stdset


【解决方案1】:

walnut@ 在 cmets 中链接了一些很棒的建议。其中,在您的特定情况下,我更喜欢使用map 而不是set,因为映射键对应于节点之间的显式弧。下面的片段显示了这在您的示例中的外观(我将您的遍历代码放入Graph::f):

#include <set>
#include <map>

class Node {
private:
    unsigned id_;
    bool marked_;
    std::map<unsigned, Node> neighbors_;

    bool is_marked() const;
    void set_marked(bool val);
    unsigned id() const { return id_; }

    friend class Graph;
public:
    bool operator== (const Node& other) const { return id_ == other.id_; }
    bool operator< (const Node& other) const { return id_ < other.id_; }
};


class Graph {
private:
    std::map<unsigned, Node> vertices_;
    void reach_set_helper(unsigned id, std::vector<unsigned> &reach_set);
    void f(Node& s);
};

void Graph::f(Node& s)
{
  auto neighbors = vertices_.find(s.id())->second.neighbors_;
  for (auto it = neighbors.begin(); it != neighbors.end(); ++it) {
    if (!it->second.is_marked()) {
        reach_set.push_back(it->first);
        it->second.set_marked(true);
    }
    this->reach_set_helper(it->first, reach_set);
  }
}

如上所示,您可以在需要 id 时选择使用 it-&gt;second.id()it-&gt;first,因为它们的值相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-15
    • 2018-02-13
    • 1970-01-01
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    • 2020-11-27
    相关资源
    最近更新 更多