具有动态范围的多维数组(无原始指针)

时间:2018-11-28 12:21:27

标签: c++ multidimensional-array boost

所述阵列背后的原理是模拟2D / 3D像素矩阵。

经过大量研究和阅读后,我认为Boost.MultiArray可能会派上用场。但是,我仍然必须在其顶部创建一个简洁的包装,以减少冗长的编码。

最终,我想实现以下目标:

PixMat<u8, 3, {2, 4, 3}> pixMat;

PixMat<u8, 3> pixMat(2,3,4);

,它将基本上创建一个2x4x3的u8值矩阵。

到目前为止,我想出的是:

template <typename T, int Dims>
class PixMat {
public:
    typedef typename boost::multi_array<T, Dims> PixMatType;
    typedef typename PixMatType::index PixMatTypeIdx;
    PixMat(int dim1Ext, int dim2Ext) : pixMat(PixMatType(boost::extents[dim1Ext][dim2Ext])) {}
    PixMat(int dim1Ext, int dim2Ext, int dim3Ext) : pixMat(PixMatType(boost::extents[dim1Ext][dim2Ext][dim3Ext])) {}

private:
    PixMatType pixMat;
};

template <typename T>
class Pix2DMat : PixMat<T, 2> {
public:
    Pix2DMat(int dim1Ext, int dim2Ext) : PixMat<DataType, 2>(dim1Ext, dim2Ext) {}
};

template <typename T>
class Pix3DMat : PixMat<T, 3> {
public:
    Pix3DMat(int dim1Ext, int dim2Ext, int dim3Ext) : PixMat<DataType, 3>(dim1Ext, dim2Ext, dim3Ext) {}
};

我不太喜欢这种解决方案。通常,矩阵既不是2D也不是3D,但我仍然希望有一个更通用的解决方案。

问题:

  1. 是否可以通过C-TOR提供尺寸范围作为模板参数?
  2. 是否有比继承更好的方法来实现此目的,例如模板专业化,可变参数模板?但是,该如何处理而不是在整个地方都复制typedef来提升效率呢?

2 个答案:

答案 0 :(得分:1)

我认为您可以使用以下几种技术:

可变构造函数参数

您可以使用可变参数技术来创建通用的N维构造函数,而不是为每个可能的维使用单独的构造函数。在这里是您的朋友:构造器参数不需要boost::extents,而是满足Collection要求的任何东西。一个例子就是普通的STL或boost数组:

template <typename... DimSizes>
PixMat(DimSizes&&... sizes)
 : pixMat(boost::array<std::size_t, sizeof...(DimSizes)>{{ static_cast<size_t>(sizes)...}}) {
}

这不是最完善的实现;特别是对DimSizes并没有太多要求,DimSizes实际上应该都是相同的无符号整数类型(有关可能的改进,请参见this question)。同样(为简单起见),没有实现完美的转发,但这可能只需要在构造函数中将sizesstd::forward<DimSizes>(sizes)包装在一起即可。 您可以咨询this stackoverflow post以获得可能的替代实施方式。

静态声明/ SFINAE

您的模板基类具有2D构造函数和3D构造函数---或者,如果遵循上述内容,则具有模板N维构造函数---而不管实际模板参数的值如何。您可以使用静态断言或SFINAE,以便仅Dims-D维构造函数是可编译的。这会将运行时错误转换为编译错误:

template <typename... DimSizes>
PixMat(DimSizes&&... sizes)
 : pixMat(boost::array<std::size_t, sizeof...(DimSizes)>{{ static_cast<size_t>(sizes)...}}) {
    static_assert(sizeof...(DimSizes) == Dims);
}

尺寸尺寸作为模板

我认为这是可能的,尽管是完全正交的解决方案。它的实现将从上面开始,无需太多工作,但是要使其与基于构造函数自变量的解决方案互操作,将需要进行大量艰苦的工作(即,使它们成为同一类或类层次结构的一部分)。

其他库

例如,您可能想看看Eigen。它完成了许多上述艰苦的工作。

答案 1 :(得分:1)

如果我对您的理解正确,那么您需要编译时维度,但需要运行时范围。

我会使用这样的设计:

<img onMouseEnter={this.handleHover} onMouseLeave={this.handleLeave} className={`${this.state.highlight}`} src="img" />
 <img onMouseEnter={this.handleHover} onMouseLeave={this.handleLeave} className={`${this.state.highlight}`} src="img" />

数据存储在template <typename T,std::size_t Dim> class mdvector { private: std::vector<T> Data; std::array<std::size_t,Dim> Extents; private: std::size_t Offset(std::size_t const Sum) const { return Sum; } template <typename... IndexType> std::size_t Offset(std::size_t const Sum,std::size_t const Index,IndexType const... Indices) const { return Offset(Sum*Extents[Dim-sizeof...(Indices)-1u]+Index,Indices...); } public: template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>> mdvector(IndexType const... Indices): Data((... * Indices)), // c++17 fold expression Extents{Indices...} {} template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>> T const &operator()(IndexType const... Indices) const { return Data[Offset(0u,Indices...)]; } template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>> T &operator()(IndexType const... Indices) { return Data[Offset(0u,Indices...)]; } }; 中,而扩展区构成std::vector

由于我假设您要进行多维访问,因此我通过递归函数std::array使用了给定的映射。您在这方面有很大的自由度。

这是一个使用示例:

Offset

由于扩展区是动态的,因此您可以在运行时更改它们,例如:

int main()
{
mdvector<double,3u> myvec(2u,3u,4u);

for (int i= 0; i<2; ++i)
  for (int j= 0; j<3; ++j)
    for (int k= 0; k<4; ++k)
      myvec(i,j,k)= i;

for (int k= 0; k<4; ++k)
  {
  for (int i= 0; i<2; ++i)
    {
    for (int j= 0; j<3; ++j)
      std::cout << myvec(i,j,k) << "\t";
    std::cout << "\n";
    }
  std::cout << "\n";
  }
}

反之,如果您更喜欢范围作为模板参数,则它们应该在运行时保持固定。在这种情况下,您可以将它们包括在类声明中:

template <typename T,std::size_t Dim>
template <typename... IndexType,typename= std::enable_if_t<sizeof...(IndexType)==Dim>>
void mdvector<T,Dim>::Resize(IndexType const... Indices)
  { Data.resize((... * Indices)); Extents= {Indices...}; }

,但实现方式几乎相同。请注意,不必指定尺寸,因为您可以使用template <typename T,std::size_t... Indices> class mdvector { /* ... */}; 来获得尺寸。


上面的代码中的fold表达式不是严格必需的。通过以下方式可以达到相同的结果:

sizeof...(Indices)