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

如何在C++中创建nanoflann的KDTree向量?

解决nanoflann中KDTree无法存入vector的问题

问题原因

  1. 拷贝构造函数被删除:nanoflann::KDTreeSingleIndexAdaptor类明确删除了拷贝构造函数,因此无法通过push_back(const T&)这类会触发拷贝的操作将KDTree对象存入vector。
  2. 悬空引用风险:循环中创建的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:52:01