2

我正在编写代码来分析 n 维数据集。我写了一个代表任意数据集轴的类。每个轴可以有不同的数据类型,所以我选择使用模板编程。

class BaseAxis
{
};

template <class T>
class Axis : public BaseAxis
{
public:
    Axis(const T &begin, const T &end);

    const T begin;
    const T end;
};

这允许我创建任意类型的新 Axis 对象。用户可以通过添加正确的轴来配置表示整个数据集的更高级别的类 NTree,如下所示:

ntree = new ntree::NTree<float>();
ntree->AddAxis<int>(0, 8);
ntree->AddAxis<float>(-100.0f, 200.0f);

这将创建一个二维浮点数据集,整数轴范围从 0 到 8,浮点轴范围从 -100.0f 到 200.0f。要从数据集中获取值,您应该能够按如下方式查询数据结构:

ntree->Query(2, 2.5f);

我目前正在尝试弄清楚如何编写这样的函数(或可以表示这些参数的对象),因为它们可以是任意数量和类型。我读过关于 boost::variant 的文章,但我不确定如何在这种情况下应用它,因为我在 c++ 模板和库方面经验不足。

这是 NTree 类的代码的相关部分。

template <class T>
class NTree
{
public:
    NTree();
    ~NTree();

    template <class A>
    void AddAxis(const A &start, const A &end);

protected:
    std::vector<BaseAxis*> *axes;
};

template <class T>
template <class A>
void NTree<T>::AddAxis(const A &start, const A &end)
{
    BaseAxis* newAxis = new Axis<A>(start, end);
    this->axes->push_back(newAxis);
}

因此我的问题如下:如何表示一组任意长度和类型组合的值?

4

1 回答 1

1

您可以使用Typelist可变参数模板定义您的容器。这样你就可以在不使用不透明容器的情况下保持强类型安全。您可以从容器中添加和远程轴,可能以复制缓冲区为代价。

一个干净的实现可以使用每种类型的专用分配器和一个代理类来聚合它们。然后,您可以在代理类中使用模板化访问器,该访问器将使用参数将分配器转换回并调用它们的访问函数。

这是 N 维容器的(非常截断的)实现。在这种情况下,卷由相同维度但类型不同的数组列表定义,因此不是您需要的,而是关闭:

 // opaque base class for allocator
 class UnsafeAllocator{...};

 // specialized allocator:
 template<class T>
 class Allocator : public UnsafeAllocator {
      ...
      virtual T& operator[] (const size_t * position) { ... }
 };

 // proxy class for the container:
 template<class TL>
 class Volume {
 protected:
     // each field has its own allocator, allocated in the constructor
     UnsafeAllocator *opaque_allocator[Length<TL>::value];
 public:
     // put a constuctor with the current dimensions here and fill in the array of allocators using the typelist

     // typesafe getter
     template <int index> 
     typename TypeAt<TL, index>::Result &
     get(const std::initializer_list<size_t> &position){
           typedef typename TypeAt<TL, index>::Result FieldType;
           typedef Allocator<FieldType> SafeAllocator;
           SafeAllocator* allocator = dynamic_cast<SafeAllocator *>(opaque_allocator[index]);
           assert(allocator != NULL);
           return (*allocator)[position];
      }
  };

  // usage would be:
  typedef MakeTypeList(float, int, double) types;
  // define a two dimensional volume for each type
  Volume<types> volume({1024,1024});
  // example of typesafe access for the first type at position 0,0:
  float f = volume.get<0>({0,0});

此实现的主要缺点是您需要在编译时了解所有类型列表。我不知道这是否是您的规范的一部分。

如果不是,我认为没有任何方法可以让容器具有可变数量的可变参数类型,而无需在任何地方使用不透明的代理,这会破坏代码的可读性(以及可能的安全性)。

于 2013-03-19T18:33:37.100 回答