【问题标题】:range view as data member范围视图作为数据成员
【发布时间】:2019-05-10 13:05:24
【问题描述】:

我正在试用新的 range-v3 库(0.5.0,clang-7.1)

我正在遍历一个图表 (bfs)。图中的每个节点都包含一些向量数据(std::vector<double>)。在遍历图表时,我正在尝试创建一个concat_view(它是所有向量的串联)。

我正在尝试将此concat_view 存储为图形遍历类的成员变量。 (准确地说,来自 boost 图形库的default_bfs_visitor)。所以,预先,我不知道我会遇到多少向量。我正在做这样的事情。

struct bfs_visitor 
{
private:
    ranges::v3::any_view<double> mView;
public:
    template<class Graph>
    void finish_vertex (vertex_descriptor v, const Graph& g) 
    {
        auto node = g[v];
        std::vector<double>& data = dataForNode(node);
        mView = ranges::v3::concat(mView, data);
    }
};

访问完图表后,我会处理视图以提取所需信息。

由于mView 的类型随着每个concat 操作而变化,我无法在声明中明确指定mView 的类型。

This link 表示any_view 的性能受到影响。 any_view 是唯一的选择吗?

【问题讨论】:

  • 你的链接失效了
  • 你能在视图上制作 bfs_visitor 模板并让 finish_vertex 返回新的 bfs_visitor 作为 concat 的结果吗?
  • 据我所知,没有一个视图可以使一系列范围变平。如果有,您只需累积您的datas,然后变平。
  • view::join 展平一系列范围。

标签: c++ boost-graph range-v3


【解决方案1】:

你把问题钉在了头上:

  • ranges::v3::concat 的返回类型不同,因此您需要类型擦除(例如 any_view)。
  • 类型擦除的惰性组合范围在性能方面是个坏主意

在您的情况下,我会毫不犹豫地将视图替换为 reified 容器:

Live On Coliru

struct bfs_visitor 
{
private:
    std::vector<std::reference_wrapper<double> > mView;
public:
    template<class Graph>
    void finish_vertex (vertex_descriptor v, const Graph& g) 
    {
        auto& node = g[v];
        ranges::v3::push_back(mView, dataForNode(node));
    }
};

注意事项

请注意,我将auto&amp; node 作为参考,而不是复制。临时副本的连接视图是bad idea (UB)

如果您碰巧知道dataForNode(node) 没有返回对来自node 的成员数据的引用,那么这不是一个真正的问题,您可以忽略此评论。

已启用物理模式:

如果您的问题是 gGraph const&amp; 并且视图不是只读的, 要么

  • 使mViewany_view&lt;double const&gt;
  • 在访问者中存储一个指向您的图表的非常量指针并使用它来代替 g 参数

事实上,如果您根本不需要它们作为引用(这是视图的关键属性):

struct bfs_visitor 
{
private:
    std::vector<double> mCollectedData;
public:
    template<class Graph>
    void finish_vertex (vertex_descriptor v, const Graph& g) {
        ranges::v3::push_back(mCollectedData, dataForNode(g[v]));
    }
};

注意 另一件有点奇怪的事情是您的访问者模板化了Graph 参数类型,而不是vertex_descriptor。同样,如果不查看其余代码,很难知道它是否真的错了,但这肯定是不典型的。

顶点描述符类型是Graph类型的特征,所以考虑 - 写 typename Graph::vertex_descriptortypename boost::graph_traits&lt;Graph&gt;::vertex_descriptor - 使 operator() 成为非模板

【讨论】:

  • 它肯定回答了我的问题。 vertex_descriptor 确实是 typename Graph::vertex_descriptor
【解决方案2】:

可以存储any_view&lt;double&gt;,正如您在原始问题中所写的那样。

问题是您试图在运行时多次执行| ranges::view::concat(...) 操作。每次连接范围时,都会创建不同的类型。

相反,您可以使用ranges::view::join 将双精度向量序列“展平”为一个双精度范围。看到这个问题,很重要:range v3 flattening a sequence

#include <iostream>
#include <set>
#include <vector>

#include <range/v3/all.hpp>

struct graph 
{
    using node_data = std::vector<double>;
private:
    std::set<node_data> m_nodes;
    ranges::v3::any_view<double> m_view;  // Must be updated whenever a node is added/updated.

    void update_view() 
    {
        m_view = m_nodes | ranges::view::join;
    }
public:
    template<typename Container>
    graph(const Container& c) 
        : m_nodes{std::begin(c), std::end(c)}
    {
        update_view();
    }

    std::size_t size() const { return m_nodes.size(); }
    const ranges::v3::any_view<double>& data_view() const { return m_view; }
};

我尝试为join 迭代器的确切类型创建一个别名,而不是使用any_view,但我无法让它工作。 decltype 应该可以。

这将按如下方式使用 (Wandbox link):

int main()
{
    using node_data = graph::node_data;
    auto data = {node_data{0.}, node_data{1.1, 2.2}, node_data{3.3, 4.4, 5.5}, node_data{6.6, 7.7, 8.8, 9.9}};

    graph g{data};
    std::cout << "Graph size (nodes): " << g.size() << std::endl;
    std::cout << "Combined data:      ";
    auto r = g.data_view();
    for (double val : r)
    {
        std::cout << val << " ";
    }
}

输出:

Graph size (nodes): 4
Combined data:      0 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-16
    • 1970-01-01
    • 2013-04-20
    • 1970-01-01
    • 1970-01-01
    • 2010-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多