【问题标题】:Forward declaration of set Comparator集合比较器的前向声明
【发布时间】:2018-10-21 23:59:48
【问题描述】:

我有一个使用节点(顶点)的图形结构,它又以std::pair<Node*, int> 的形式附加边,其中节点是边的另一端,整数是边的权重。我想根据连接节点索引和边权重将边排序在 std::multiset 中。

enum color { white, grey, black };

struct EdgeComparator;

struct Node {
  int n;
  std::multiset<std::pair<Node *, int>, EdgeComparator> edges;
  enum color col;
  int d;  // distance to source node

  explicit Node(int n) : n(n), edges(), col(white), d() {};
};

struct EdgeComparator {
  bool operator()(const std::pair<Node *, int> &p1,
                  const std::pair<Node *, int> &p2) {
    if (p1.second == p2.second)
      return p1.first->n < p2.first->n;
    return p1.second < p2.second;
  }
};

这种前向声明方法会导致错误:invalid use of incomplete type struct EdgeComparator。如果我尝试切换它们并转发声明 Node 而不是 EdgeComparator,则 EdgeComparator 将不再看到 n 字段,因此我陷入了一个恶性循环。

我想到的唯一解决方法是使用std::vector 而不是std::multiset,然后应用std::sort,但这在效率方面会非常昂贵,所以我想知道是否有其他方法。

【问题讨论】:

    标签: c++ graph forward-declaration cross-reference


    【解决方案1】:

    你可以这样做:

    #include <set>
    
    enum color { white, grey, black };
    
    struct Node;
    
    struct EdgeComparator {
      bool operator()(const std::pair<Node *, int> &p1,
                  const std::pair<Node *, int> &p2);
    };
    
    struct Node {
      int n;
      std::multiset<std::pair<Node *, int>, EdgeComparator> edges;
      enum color col;
      int d;  // distance to source node
    
      explicit Node(int n) : n(n), edges(), col(white), d() {};
    };
    
    bool EdgeComparator::operator()(const std::pair<Node *, int> &p1,
                  const std::pair<Node *, int> &p2) {
      if (p1.second == p2.second)
        return p1.first->n < p2.first->n;
      return p1.second < p2.second;
    }
    

    这在我这边编译得很好。原因是,您拆分了声明和定义。 EdgeComparator::operator()的定义需要具体结构Node,声明不需要,只需要知道是否存在同名结构即可:

    1. Forward 将 Node 声明为结构体(EdgeComparator 声明时需要)
    2. 在没有定义 operator() 的情况下声明 EdgeComparator(需要知道成员 Node::n)
    3. 声明和定义节点
    4. 定义 EdgeComparator::operator()

    【讨论】:

      【解决方案2】:

      EdgeComparator 设为模板参数。

      首先,它解决了您的情况。其次,它是有意义的,并且允许您提供另一种类型的比较器。

      template<class TEdgeComparator>
      struct Node {
        int n;
        std::multiset<std::pair<Node *, int>, TEdgeComparator> edges;
        // ...
      };
      
      struct EdgeComparator {
        bool operator()(const std::pair<Node *, int> &p1,
                        const std::pair<Node *, int> &p2) {
          if (p1.second == p2.second)
            return p1.first->n < p2.first->n;
          return p1.second < p2.second;
        }
      };
      
      Node<EdgeComparator> myNode(42);
      

      但请记住,节点包含边的集合是一个危险信号。你确定你的设计吗?

      【讨论】:

      • 感谢您的观察。该设计有助于我的实现,也许我选择使用“边缘”这个词并不是很有灵感。如果说得通的话,它更像是“邻居”。
      • @IoanaAlexandru A node is a node. A neighborhood is a neighborhood。在您迷失在软件遗忘中之前,请确保您走的是正确的道路。
      • 您有更好的会员名称吗?我愿意接受建议,我喜欢保持我的代码干净,易于理解。
      • @IoanaAlexandru 将Node 拆分为两个类。 SOLID 你的摩托车。
      猜你喜欢
      • 2020-10-29
      • 2013-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多