如何在C++中创建nanoflann的KDTree向量?
解决nanoflann中KDTree无法存入vector的问题
问题原因
- 拷贝构造函数被删除:
nanoflann::KDTreeSingleIndexAdaptor类明确删除了拷贝构造函数,因此无法通过push_back(const T&)这类会触发拷贝的操作将KDTree对象存入vector。 - 悬空引用风险:循环中创建的
PointCloud<double>是局部变量,KDTree构造时会持有对该对象的引用,局部变量销毁后,KDTree内的引用会变成悬空引用,后续使用会导致未定义行为。
解决方案
方案一:使用智能指针存储(推荐)
通过std::unique_ptr管理KDTree的生命周期,避免拷贝操作,同时将PointCloud持久化存储,确保其生命周期不短于KDTree:
#include <string> #include <vector> #include <memory> #include <nanoflann.hpp> using std::string; using std::vector; using std::unique_ptr; template <typename T=double> struct Point { T x; T y; T z; }; template <typename T> struct PointCloud{ vector<Point<double>> points; inline size_t kdtree_get_point_count() const { return points.size(); } inline double kdtree_get_pt(const size_t idx, int dim) const { switch(dim) { case 0: return points[idx].x; case 1: return points[idx].y; case 2: return points[idx].z; default: return static_cast<T>(0.0); } } template <class BBOX> bool kdtree_get_bbox(BBOX& /* bb */) const { return false; } }; using KDTree = nanoflann::KDTreeSingleIndexAdaptor< nanoflann::L2_Simple_Adaptor<double, PointCloud<double>>, PointCloud<double>, 3>; int main() { vector<Point<double>> points1 {{0.0, 0.0, 0.0}, {0.0, 1.0, 1.0}, {1.0, 0.0, -1.0}}; vector<Point<double>> points2 {{0.3, 0.4, 0.0}, {0.0, 1.0, 1.0}, {1.0, 0.0, -1.0}}; vector<Point<double>> points3 {{0.0, 0.8, 0.9}, {0.3, 1.0, 1.1}, {1.9, 0.3, -1.0}}; vector<vector<Point<double>>> points_sets {points1, points2, points3}; vector<PointCloud<double>> point_clouds; vector<unique_ptr<KDTree>> kdtrees; nanoflann::KDTreeSingleIndexAdaptorParams params{10}; kdtrees.reserve(points_sets.size()); point_clouds.reserve(points_sets.size()); for(const auto& pts : points_sets) { point_clouds.emplace_back(); point_clouds.back().points = pts; kdtrees.emplace_back(new KDTree(3, point_clouds.back(), params)); kdtrees.back()->buildIndex(); } // 示例查询:查询点(0.1,0.1,0.1)到第一个KDTree的最近邻 const double query_pt[] = {0.1, 0.1, 0.1}; size_t ret_index; double out_dist_sqr; nanoflann::KNNResultSet<double> result_set(1); result_set.init(&ret_index, &out_dist_sqr); kdtrees[0]->findNeighbors(result_set, query_pt, params); }
方案二:直接在vector中构造KDTree
提前为vector预留空间,避免扩容时触发拷贝,同时持久化存储PointCloud:
#include <string> #include <vector> #include <nanoflann.hpp> using std::string; using std::vector; template <typename T=double> struct Point { T x; T y; T z; }; template <typename T> struct PointCloud{ vector<Point<double>> points; inline size_t kdtree_get_point_count() const { return points.size(); } inline double kdtree_get_pt(const size_t idx, int dim) const { switch(dim) { case 0: return points[idx].x; case 1: return points[idx].y; case 2: return points[idx].z; default: return static_cast<T>(0.0); } } template <class BBOX> bool kdtree_get_bbox(BBOX& /* bb */) const { return false; } }; using KDTree = nanoflann::KDTreeSingleIndexAdaptor< nanoflann::L2_Simple_Adaptor<double, PointCloud<double>>, PointCloud<double>, 3>; int main() { vector<Point<double>> points1 {{0.0, 0.0, 0.0}, {0.0, 1.0, 1.0}, {1.0, 0.0, -1.0}}; vector<Point<double>> points2 {{0.3, 0.4, 0.0}, {0.0, 1.0, 1.0}, {1.0, 0.0, -1.0}}; vector<Point<double>> points3 {{0.0, 0.8, 0.9}, {0.3, 1.0, 1.1}, {1.9, 0.3, -1.0}}; vector<vector<Point<double>>> points_sets {points1, points2, points3}; vector<PointCloud<double>> point_clouds; vector<KDTree> kdtrees; nanoflann::KDTreeSingleIndexAdaptorParams params{10}; // 预留空间,避免vector扩容时拷贝KDTree kdtrees.reserve(points_sets.size()); point_clouds.reserve(points_sets.size()); for(const auto& pts : points_sets) { point_clouds.emplace_back(); point_clouds.back().points = pts; // 直接在vector内存中构造KDTree kdtrees.emplace_back(3, point_clouds.back(), params); kdtrees.back().buildIndex(); } }
关键注意事项
- 必须保证
PointCloud对象的生命周期长于对应的KDTree,否则KDTree会持有悬空引用,导致程序崩溃或未定义行为。 - 若使用
vector<KDTree>,一定要提前调用reserve预留足够空间,防止vector扩容时尝试拷贝KDTree对象(KDTree不支持拷贝)。
内容的提问来源于stack exchange,提问作者EmanyAlpsid
相关产品推荐
相关产品推荐

