特征分配器给模板类带来麻烦

Eigen Allocator causing Trouble in Templated Class

本文关键字:麻烦 分配器 特征      更新时间:2023-10-16

我有这个类:

typedef vector<Eigen::Affine3d,Eigen::aligned_allocator<Eigen::Affine3d> > VoteList;
template <class T>
class KernelDensityEstimator {
  public:
    KernelDensityEstimator() {}
    void setData(vector<T>& d) {
      data = d;
    }
    void setKernel(double (*k)(T&, T&)) {
      kernel = k;
    }
    double evaluate(T& p) {
      double sum;
      for (int i = 0; i < data.size(); i++) {
        sum += (*kernel)(p, data[i]);
      }
      return sum/data.size();
    }
  private:
    double (*kernel) (T&, T&);
    vector<T> data;
};

我现在想与类型T=Eigen::Affine3d一起使用。然而,当我调用setData()时,它给我带来了麻烦,因为Eigen需要为std容器指定一个Eigen::aligend_allocater来与Eigen一起使用。

因此,当我将vector<Eigen::Affine3d,Eigen::aligned_allocator<Eigen::Affine3d> >(也称为VoteList)作为setData()的输入参数时,我的编译器抱怨道:

no known conversion for argument 1 from ‘VoteList {aka std::vector<Eigen::Transform<double, 3, 2>, Eigen::aligned_allocator<Eigen::Transform<double, 3, 2> > >}’ to ‘std::vector<Eigen::Transform<double, 3, 2>, std::allocator<Eigen::Transform<double, 3, 2> > >&’

这是有道理的,但我认为分配器是对象类型的一部分。有没有办法绕过这一点并保持我的KernelDensityEstimator通用性?

您可以为计划使用的类型启用Eigen的std::vector专业化,如图所示,例如:

#include<Eigen/StdVector>
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Affine3d)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Matrix4d)
...

您基本上希望根据类模板参数类型T为成员data提供不同的类型。有几种方法可以做到这一点,这里有一个建议:

  • 为类定义一种特殊的存储类型,在其中保存数据(最好将其放在合适的命名空间中或类内):

    //normal storage type
    template<typename T>
    struct storage_type_impl
    {
        using type = std::vector<T>;
    };
    //storage type for Eigen::Transform types
    template<typename ... Args>
    struct storage_type_impl<Eigen::Transform<Args ...> >
    {
        using type = std::vector<Eigen::Transform<Args ...>
                               , Eigen::aligned_allocator<Eigen::Transform<Args ...> > >;
    };
    template<typename T>
    using storage_type = typename storage_type_impl<T>::type;
    
  • 现在,您可以在类中使用它,再加上模板化的setData方法(这就足够了,因为如果编译器无法执行数据复制/移动,它仍然会抱怨):

    template <class T>
    struct KernelDensityEstimator
    {
        template<typename D>
        auto setData(D&& d)
        {
            data = std::forward<D>(d);  //calls move assignment if d is an rvalue
                                        //otherwise a copy is performed
        }
        //...
    private:
        storage_type<T> data;
        //...
    };    
    

该代码未经测试,因为我目前没有可用的Eigen,但我希望它仍然能够解释基本思想。