作为数据成员的范围视图

range view as data member

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

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

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

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是唯一的选择吗?

你一针见血:

  • ranges::v3::concat 的 return 类型不同,因此您需要 type-erasure(例如 any_view)。
  • 类型擦除惰性组合范围是个坏主意performance-wise

在你的情况下,我会毫不犹豫地用 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));
    }
};

NOTE IMPORTANT

Note that I made auto& node a reference, instead of taking a copy. Concatenating views of temporary copies is a bad idea (UB).

If you happen to know that dataForNode(node) does NOT return a reference to member data from node, then this is not a real issue and you can disregard this comment.

PHYSIC MODE ENGAGED:

If your issue was that g is Graph const& and the view is not readonly, either

  • make mView an any_view<double const>
  • store a non-const pointer to your graph in the visitor and use that instead of the g argument

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

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]));
    }
};

Note Another slightly weird thing is that your visitor templates the Graph argument type, but not the vertex_descriptor. Again, it's hard to know whether it's actually wrong without seeing the rest of the code, but it's certainly a-typical.

The vertex descriptor type is characteristic of the Graph type, so consider - writing typename Graph::vertex_descriptor or typename boost::graph_traits<Graph>::vertex_descriptor - making the operator() non-template

可以存储any_view<double>,正如您在原始问题中所写。

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

相反,您可以使用 ranges::view::join 到 "flatten" 双精度向量序列,进入双精度范围。看到这个问题,这很重要:

#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