我想通过编写自己的vistor来了解Visitor concepts如何在boost图库中工作。 base visitor
和time_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年进行了更改wikipedia。time_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;
};