You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Boost中正确使用indexable?两种rtree.query实现方案对比

两种Boost.Geometry RTree索引方案的对比选择

我实现了两种让rtree.query生效的方法,分别是:

  • 为Entity<float>特化indexable
  • 使用方法包装器

想请教在这个场景下哪种方案更优?

#include <SFML/Graphics.hpp>
#include <iostream>
#include <sstream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <random>

#define METHOD_WRAPPER 0
#define SPECIALIZATION 1
#define SOLUTION METHOD_WRAPPER

using point = boost::geometry::model::d2::point_xy<float>;

template<typename T, typename IndexableGetter=boost::geometry::index::indexable<T>>
class Collection {
public:
    using box = boost::geometry::model::box<point>;
    void insert(T const& t) { rtree.insert(t); }
    auto begin() const { return rtree.begin(); }
    auto end() const { return rtree.end(); }
    auto find(auto const& search, float radius) {
        std::vector<std::reference_wrapper<T const>> result;
        rtree.query(boost::geometry::index::satisfies([&](auto const& el) {
            return boost::geometry::distance(el.position, search) < radius ;
        }), std::back_inserter(result));
        return result;
    }

private:
    boost::geometry::index::rtree<T, boost::geometry::index::quadratic<32>, IndexableGetter> rtree;
};


template<typename T>
struct Entity {
    using result_type = point;
    point position;
    Entity(T x, T y) : position(x, y) {}
#if SOLUTION == METHOD_WRAPPER
    struct ByPosition {
        using result_type = point;
        result_type const& operator()(Entity const& boid) const { return boid.position; }
    };
#endif
    operator sf::Vector2f() const { return sf::Vector2f(position.x(), position.y()); }
};

#if SOLUTION == SPECIALIZATION
namespace boost { namespace geometry { namespace index {

template<>
struct indexable<Entity<float>> {
    using result_type = point;
    result_type operator()(Entity<float> const& e) const {
        return result_type(e.position);
    }
};

}}}
#endif

int main() {
    using Entity = Entity<float>;

    #if SOLUTION == METHOD_WRAPPER
    Collection<Entity, Entity::ByPosition> collection;
    #elif SOLUTION == SPECIALIZATION
    Collection<Entity> collection;
    #endif

    for (int i = 0; i < 10; i++)
        collection.insert(Entity(
            static_cast<float>(rand() % 100),
            static_cast<float>(rand() % 100))
        );

    for (auto& el : collection)
        std::cout << el.position.x() << ", " << el.position.y() << std::endl;

    auto result = collection.find(point(50, 50), 30);
    for (auto& el : result)
        std::cout << el.get().position.x() << ", " << el.get().position.y() << std::endl;
}

两种方案的优劣分析

1. 特化indexable方案(SPECIALIZATION)

  • 优势:
    • 使用简洁:实例化Collection时无需额外指定模板参数,代码调用更直观。
    • 全局一致性:特化后所有用到Entity<float>的RTree都会自动应用该索引规则,无需重复配置。
  • 劣势:
    • 灵活性差:全局特化是唯一的,若后续需要为Entity<float>基于其他属性索引,会直接冲突。
    • 命名空间污染:必须在boost::geometry::index命名空间下定义特化,可能与其他第三方代码的特化逻辑冲突,也破坏了命名空间的整洁性。

2. 方法包装器方案(METHOD_WRAPPER)

  • 优势:
    • 扩展性强:同一个Entity类型可根据不同需求传入不同的索引器,比如需要基于其他属性索引时,只需新增一个内部包装器结构体即可。
    • 逻辑内聚:索引器作为Entity的内部类,属于类型自身的一部分,不会污染全局或第三方命名空间。
  • 劣势:
    • 代码稍繁琐:实例化Collection时必须显式指定索引器类型,若多处使用同一索引器,可通过typedef简化,但仍需额外定义。

方案选择建议

  • 如果你的Entity<float>在所有业务场景下都只需要基于position属性做索引,特化方案更简洁省心。
  • 如果未来可能需要为同一Entity类型提供多种索引方式,或者希望避免污染第三方命名空间,方法包装器方案的灵活性和扩展性更适合。

内容的提问来源于stack exchange,提问作者nowox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 02:13:17