为什么在boost图库中的Visitor中使用CRTP模式?

时间:2018-05-08 15:24:40

标签: c++ boost boost-graph

我想通过编写自己的vistor来了解Visitor concepts如何在boost图库中工作。 base visitortime_stamper EventVisitor的{​​{3}}是,

  // needed for MSVC workaround
  template <class Visitor>
  struct base_visitor {
    typedef on_no_event event_filter;
    template <class T, class Graph>
    void operator()(T, Graph&) { }
};

  template <class TimeMap, class TimeT, class Tag>
  struct time_stamper
    : public base_visitor<time_stamper<TimeMap, TimeT, Tag> >
  {
    typedef Tag event_filter;
    time_stamper(TimeMap pa, TimeT& t) : m_time_pa(pa), m_time(t) { }
    template <class Vertex, class Graph>
    void operator()(Vertex u, const Graph&) {
      put(m_time_pa, u, ++m_time);
    }
    TimeMap m_time_pa;
    TimeT& m_time;
};

time_stamper是从基类base_visitor派生的类。基类的模板参数的类型是派生类本身。我相信这种模式被称为奇怪的重复模板模式(CRTP)。

我的问题是,为什么在这里使用CRTP? source code页面显示CRTP通常用于实现静态多态,但在这种情况下它看起来并非如此。

我自己实现了一个简单的访问者。

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/random.hpp>
#include <iostream>
#include <random>

using namespace boost;
using Graph = adjacency_list<vecS, vecS, directedS>;

struct tree_edge_event_visitor
  : public base_visitor<tree_edge_event_visitor> // works without this line
{
    using event_filter = on_tree_edge;
    template <class Edge, class Graph>
    void operator()(Edge u, Graph const& g)
    {   
        std::cout << source(u, g) << "->" << target(u, g) << '\n';
    }   
};

int main()
{
    Graph g;
    std::random_device rd; 
    std::mt19937 engine(rd());
    generate_random_graph(g, 10, 16, engine);
    write_graphviz(std::cout, g); 

    std::vector<default_color_type> color(num_vertices(g));
    std::cout << "Writing out tree edges: ";
    depth_first_visit(g, 0, dfs_visitor(tree_edge_event_visitor()),
        make_iterator_property_map(color.begin(), get(vertex_index, g)));

    return 0;
}

它确实有效,并且它继续工作没有派生自base_visitor(请参阅代码中的注释)。那么为什么CRTP在这里使用?评论need for MSVC workround似乎表明此模式仅适用于MSVC。

更新:在2000年进行了更改wikipediatime_stamper的旧版本不使用CRTP。我认为CRTP使用的基本原理特定于非常旧版本的MSVC。不幸的是,我无法在现代MSVC中测试它,因为我没有Windows机器。

  template <class TimePA, class TimeT, class Tag>
  struct time_stamper {
    typedef Tag event_filter;
    time_stamper(TimePA pa, TimeT& t) : m_time_pa(pa), m_time(t) { }
    template <class Vertex, class Graph>
    void operator()(Vertex u, const Graph& g) {
      put(m_time_pa, u, ++m_time);
    }
    TimePA m_time_pa;
    TimeT& m_time;
};

0 个答案:

没有答案