表示为1D的多维数组(对于n维进行模板化)

时间:2018-05-05 12:50:40

标签: c++ arrays multidimensional-array c++17

在C ++中声明具有静态大小的多维数组非常容易,然后数组存储在一个连续的内存块中(行主要布局)。

问题

然而,在C ++中声明动态分配的多维数组(仅在运行时已知的大小)非常棘手,如in other SO thread regarding arrays所述。要使用多个方括号保留相同的语法(在2D数组的情况下),您需要创建一个指针数组,指向另一组数组(行)。使用更多维度会增加更多(不必要的)间接级别,内存碎片,并且对于小型数组,指针可以占用比实际数据更多的内存。

其中一个解决方案是使用1D数组,然后重新计算索引。

实施例

大小为10,3和5的3D数组。我想要一个位置为3,1,4的元素而不是写3darray[3][1][4]我会写3darray[index],其中index将被计算为{{1当被替换时,结果为3*(y_dym_size*z_dym_size) + 1*(z_dym_size) + 4

我可以很容易地创建一个封装动态分配数组的类,并以呈现的方式重新计算索引,但这不实用,因为需要为每个维度编写。

问题:

我想创建一个适用于任意数量维度且零开销的模板(这是现代C ++的精神 - 具有可重用的代码/类,其中更多的工作正在转移到编译器)。我有以下代码适用于n维数组,但没有0开销。它包含for循环,并且还有一个在1D分辨率中使用的数组:

3*(3*5)+1*(5)+4

我想到的其他方法是使用可变参数模板,这有希望 - 在编译器优化之后 - 与专门为某些维度编写的代码相同:

template <class T, size_t DIM>
class arrayND{
    std::array<size_t, DIM> sizes;
    std::array<size_t, DIM-1> access_multiplier;
    vector<T> data;

  public:
    using iterator = typename vector<T>::iterator;
    using const_iterator = typename vector<T>::const_iterator;

    template <typename... Args, typename std::enable_if_t<sizeof...(Args) == DIM, int> = 0>
    arrayND(Args&&... args) {
        std::array<size_t, DIM> temp{args...};
        sizes = temp;
        size_t mult = 1;
        for(int i = DIM-2; i >= 0; --i){
            mult *= sizes[i+1];
            access_multiplier[i] = mult;
        }
        data.resize(mult*temp[0]);
    }

    template <typename... Args, typename std::enable_if_t<sizeof...(Args) == DIM, int> = 0>
    T& get(Args&&... args){
        std::array<size_t, DIM> idx_copy{args...};
        size_t index = idx_copy[DIM-1];
        for(int i = DIM-2; i >= 0; --i){
            index += idx_copy[i]*access_multiplier[i];
        }
        return data[index];
    }

    template <typename... Args, typename std::enable_if_t<sizeof...(Args) == DIM, int> = 0>
    T& operator()(Args&&... args){
        return get(args...);
    }

    void set(const T& elem){
        fill(begin(data), end(data), elem);
    }

    iterator begin(){
        return begin(data);
    }

    iterator end(){
        return end(data);
    }

    const_iterator begin() const{
        return cbegin(data);
    }

    const_iterator end() const{
        return cend(data);
    }
};

在当前版本(C ++ 17)或C ++语言中是否有办法如何获得灵活性(任意数量的维度)和性能(零开销与专门为某些维度编写的代码相比)?如果必须有开销,那么硬编码更有意义,因为最多可以说5个维度。 在一些现有的库中是否已经实现了动态多维数组?

1 个答案:

答案 0 :(得分:1)

从存储中拆分视图。

T的n维数组视图是一个带有T指针的类,以及获得n-1步幅大小的某种方式。 []返回一个n-1维数组视图。

这种观点有两种不同的风格。第一个存储步幅,第二个指向连续的步幅缓冲区。两者都有其优点;当一些或所有尺寸固定时,第一个小心甚至可以优化。但我会做第二次。

template<class T, std::size_t N>
struct slice {
  T* ptr=0;
  std::size_t const* strides=0;
  slice<T,N-1> operator[]( std::size_t i )const{
    return { ptr + i**strides, strides+1 };
  }
};
template<class T>
struct slice<T,1> {
  T* ptr=0;
  std::size_t const* strides=0;
  T& operator[]( std::size_t i )const{
    return *(ptr + i**strides);
  }
};

这个允许每个元素的步幅。

现在,您只需公开stride<T,N>即可进行链式[]。这类似于我为3维度编写的方式。

如果您更喜欢(x,y,z)语法,并且您唯一的问题是for循环并且害怕编译器没有将其展平,您可以使用pack扩展来强制扁平化。但首先要对轮廓进行分析和检查。