【发布时间】:2018-05-05 12:50:39
【问题描述】:
在 C++ 中声明具有静态大小的多维数组非常容易,然后将数组存储在一个连续的内存块中(行主要布局)。
问题
然而,正如in other SO thread regarding arrays 所讨论的那样,在 C++ 中声明动态分配的多维数组(大小仅在运行时知道)非常棘手。要使用多个方括号(在二维数组的情况下)保留相同的语法,您需要创建一个指针数组,指向另一组数组(行)。随着维度的增加,它会增加更多(不必要的)间接级别、内存碎片,并且对于较小的数组,指针可能会占用比实际数据更多的内存。
其中一种解决方案是使用一维数组,然后重新计算索引。
示例:
大小为 10、3 和 5 的 3D 数组。我想要一个位于位置 3、1、4 的元素,而不是写3darray[3][1][4] 我会写3darray[index],其中索引将计算为3*(y_dym_size*z_dym_size) + 1*(z_dym_size) + 4,当被替换时, 结果到3*(3*5)+1*(5)+4。
我可以轻松地创建一个封装动态分配的数组并以呈现的方式在索引中重新计算的类,但这并不实用,因为它需要针对每个维数编写。
问题:
我想创建一个模板,该模板适用于任意数量的维度且开销为零(这是现代 C++ 的精神 - 具有可重用的代码/类,其中更多的工作被转移到编译器)。我有以下适用于 n 维数组的代码,但是没有 0 开销。它包含 for 循环,并且还有一个用于 1D 分辨率的数组:
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);
}
};
我正在考虑的其他方法是使用可变参数模板,希望在编译器优化之后与专门为某些维度编写的代码相同:
int getIndex(size_t index){
return index;
}
template<typename... Args>
int getIndex(size_t index, Args... args){
return access_multiplier[DIM-sizeof...(Args)-1]*index + getIndex(args...);
}
template <typename... Args, typename std::enable_if_t<sizeof...(Args) == DIM, int> = 0>
T& get(Args&&... args){
return data[getIndex(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];*/
}
在当前版本 (C++17) 或 C++ 语言中是否有办法同时获得灵活性(任意维数)和性能(零开销与专门为某些维数编写的代码相比)?如果必须有开销,那么对它进行硬编码更有意义,比如最多 5 个维度。 一些现有的库中是否已经实现了动态多维数组?
【问题讨论】:
-
静态大小的数组不需要向量。并且有很多方法可以使用
[]来实现它们。要么您感到困惑,要么存在需要您进行体操的限制。没有明确的约束很难解决问题。 -
@Yakk:他想成为abe来声明
arrayND<3> small(2,2), big(200,200);- 维度的数量是类型的一部分,但是每个维度的大小是传递给构造函数的。这需要一个向量(或其他动态分配的缓冲区)。 -
感谢 Martin 提供的示例,正如您所写的那样。 @yakk我已经更新了这个问题,明确指出这个问题是关于动态分配的数组(我错过了)。
标签: c++ arrays multidimensional-array c++17