Запрос Boost::geometry возвращает индексы

Я хочу иметь класс, который использует boost::geometry::index::rtree для пространственных индексаторов. Только этот класс должен знать о повышении, поэтому я использую что-то вроде этого:

struct VeryImportantInfo
{
    ...
    float x;
    float y;
}

class Catalogue
{
    ...
public:
    std::vector<std::shared_ptr<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);

protected:
    using point = bg::model::point<float, 2, bg::cs::cartesian>;
    using value = std::pair<point, std::shared_ptr<VeryImportantInfo> >;
    using box = bg::model::box<point>;        

    boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
}

std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
    box query_box(point(x1, y1), point(x2, y2));
    ???
}

Я не знаю, как правильно выполнить запрос (Пожалуйста, не смотрите на этот ужасный вектор, возвращаемый копией, это просто для примера). Я могу сделать это:

std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
    box query_box(point(x1, y1), point(x2, y2));
    std::vector<value> result_s;
    rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));
    std::vector<std::shared_ptr<VeryImportantInfo> > results;
    results.reserve(result_s.size());
    for( auto& p : result_s)
    {
        results.emplace_back(p.second);
    }
    return results;
}

Я хочу знать, как мне избавиться от внутренней копии (не возвращать копию, results.emplace_back(p.second); - вот эту). Потому что у меня может быть более 10 000 результатов в result_s, и это будет пустой тратой времени.

Благодарю вас!


person DoctorMoisha    schedule 07.05.2015    source источник


Ответы (1)


Обновить комментарий

Если беспокойство было в первую очередь о временном векторе, просто не используйте его. Вы можете использовать qbegin()/qend() бесплатные функции из boost::geometry::index:

std::vector<std::shared_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
    box query_box(point(x1, y1), point(x2, y2));

    auto b = bgi::qbegin(rtree, bgi::intersects(query_box)), 
        e = bgi::qend(rtree);

    auto range  = boost::make_iterator_range(b, e);

    using namespace boost::adaptors;
    return boost::copy_range<std::vector<std::shared_ptr<VeryImportantInfo>>>(
            range | transformed([](value const& p) { return p.second; }));
}

На самом деле, если известно, что rtree является константой, вы можете даже напрямую вернуть ленивый диапазон и не выделять даже один вектор.


Исходный/старый текст ответа следует ниже:


Вы не можете копировать общие указатели без подсчета ссылок.

Конечно, вы можете изменить пару value, чтобы она содержала ссылку на shared_ptr, но вместо этого вы могли бы использовать либо необработанные ссылки (std::reference_wrapper), либо weak_ptr.

std::reference_wrapper<T>

Вот мой взгляд на необработанные ссылки (просто держите свои важные данные поблизости :)):

Жить на Coliru

#include <iostream>
#include <vector>

#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/index/rtree.hpp>

namespace bg  = boost::geometry;
namespace bgi = bg::index;

struct VeryImportantInfo {
    float x;
    float y;
};

VeryImportantInfo a = { 2, 2 };
VeryImportantInfo b = { 3, 3 };
VeryImportantInfo c = { 4, 4 };

class Catalogue
{
public:
    Catalogue() {
        rtree.insert(value(point(a.x, a.y), a));
        rtree.insert(value(point(b.x, b.y), b));
        rtree.insert(value(point(c.x, c.y), c));
    }

    std::vector<std::reference_wrapper<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);

protected:
    using point = bg::model::point<float, 2, bg::cs::cartesian>;
    using value = std::pair<point, std::reference_wrapper<VeryImportantInfo> >;
    using box   = bg::model::box<point>;

    boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
};

std::vector<std::reference_wrapper<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
    box query_box(point(x1, y1), point(x2, y2));

    std::vector<value> result_s;
    rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));

    std::vector<std::reference_wrapper<VeryImportantInfo> > results;
    results.reserve(result_s.size());

    for(auto& p : result_s) {
        results.push_back(p.second);
    }
    return results;
}

int main() {
    Catalogue cat;
    for (VeryImportantInfo& vii : cat.FindIn(1,2,3,4))
        std::cout << vii.x << ", " << vii.y << "\n";
}

std::weak_ptr<T>

Вот то же самое с weak_ptr<>. Можно утверждать, что это мало что решает (поскольку подсчет ссылок все еще происходит), но, по крайней мере, требуется меньше работы.

Жить на Coliru

#include <iostream>
#include <memory>
#include <vector>

#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/index/rtree.hpp>

namespace bg  = boost::geometry;
namespace bgi = bg::index;

struct VeryImportantInfo {
    float x;
    float y;
};

auto a = std::make_shared<VeryImportantInfo>(VeryImportantInfo{2, 2});
auto b = std::make_shared<VeryImportantInfo>(VeryImportantInfo{3, 3});
auto c = std::make_shared<VeryImportantInfo>(VeryImportantInfo{4, 4});

class Catalogue
{
public:
    Catalogue() {
        rtree.insert(value(point(a->x, a->y), a));
        rtree.insert(value(point(b->x, b->y), b));
        rtree.insert(value(point(c->x, c->y), c));
    }

    std::vector<std::weak_ptr<VeryImportantInfo> > FindIn(float x1, float x2, float y1, float y2);

protected:
    using point = bg::model::point<float, 2, bg::cs::cartesian>;
    using value = std::pair<point, std::shared_ptr<VeryImportantInfo> >;
    using box   = bg::model::box<point>;

    boost::geometry::index::rtree< value, bgi::quadratic<16> > rtree;
};

std::vector<std::weak_ptr<VeryImportantInfo> > Catalogue::FindIn(float x1, float y1, float x2, float y2)
{
    box query_box(point(x1, y1), point(x2, y2));

    std::vector<value> result_s;
    rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));

    std::vector<std::weak_ptr<VeryImportantInfo> > results;
    results.reserve(result_s.size());

    for(auto& p : result_s) {
        results.push_back(p.second);
    }
    return results;
}

int main() {
    Catalogue cat;
    for (auto& vii_p : cat.FindIn(1,2,3,4))
        if (auto vii = vii_p.lock())
            std::cout << vii->x << ", " << vii->y << "\n";
}
person sehe    schedule 07.05.2015
comment
Это действительно приятно! Но я должен подчеркнуть это больше: основная проблема в том, что я могу иметь >10k объектов в result_s векторе, так что это не проблема подсчета ссылок, вся копия этого массива будет большой тратой. - person DoctorMoisha; 08.05.2015
comment
Просто вернуть вектор как есть? Или используйте адаптер диапазона map_values. - person sehe; 08.05.2015
comment
Как я могу использовать map_values? - person DoctorMoisha; 08.05.2015