Я пробую новую библиотеку range-v3 (0.5.0, clang-7.1)

Я перебираю график (BFS). Каждый узел в графе содержит некоторые векторные данные (std::vector<double>). Проходя по графику, я пытаюсь создать concat_view (который объединяет все векторы).

Я пытаюсь сохранить это concat_view как переменную-член класса обхода графа. (Если быть точным, 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 в объявлении.

По этой ссылке говорится, что производительность any_view снижается. any_view единственный вариант?

3
Surya 10 Май 2019 в 16:05

2 ответа

Лучший ответ

Вы прибиваете проблему на голову:

  • возвращаемый тип 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& node ссылку вместо того, чтобы взять копию. Объединение представлений временных копий является плохой идеей (UB).

Если вам известно, что dataForNode(node) НЕ возвращает ссылку на данные члена из node, то это не настоящая проблема, и вы можете игнорировать этот комментарий.

Включен физический режим .

Если ваша проблема заключалась в том, что g равен Graph const&, и представление не доступно только для чтения, либо

  • сделать mView any_view<double const>
  • сохраните неконстантный указатель на ваш график в посетителе и используйте его вместо аргумента 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_descriptor или typename boost::graph_traits<Graph>::vertex_descriptor - сделать оператор () не шаблонным

4
sehe 10 Май 2019 в 15:30

Вы можете хранить any_view<double>, как написали в своем первоначальном вопросе.

Проблема заключалась в том, что вы пытались выполнить операцию | ranges::view::concat(...) несколько раз во время выполнения. Каждый раз, когда диапазон конкатонируется, он создает другой тип.

Вместо этого вы можете использовать ranges::view::join, чтобы «сгладить» последовательность векторов двойников в диапазон двойников. Посмотрите на этот вопрос: range v3 выравнивает последовательность

#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):

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 
0
NicholasM 10 Окт 2019 в 07:11