如何在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
相关产品推荐
相关产品推荐

