获取多维访问的线性索引

时间:2018-06-01 23:19:13

标签: c++ variadic-templates

我试图实现一个多维std :: array,它包含一个大小为Dim-n-1 * Dim-n-2 * ... * Dim-1的连续内存数组。为此,我使用std :: array的私有继承:

Embed Interop Type

例如,constexpr std::size_t factorise(std::size_t value) { return value; } template<typename... Ts> constexpr std::size_t factorise(std::size_t value, Ts... values) { return value * factorise(values...); } template<typename T, std::size_t... Dims> class multi_array : std::array<T, factorise(Dims...)> { // using directive and some stuff here ... template<typename... Indexes> reference operator() (Indexes... indexes) { return base_type::operator[] (linearise(std::make_integer_sequence<Dims...>(), indexes...)); // Not legal, just to explain the need. } } 将访问线性索引multi_array<5, 2, 8, 12> arr; arr(2, 1, 4, 3) = 12;

我想我已经使用std :: integer_sequence,将整数序列传递给线性函数和索引列表,但我不知道该怎么做。我想要的是:

idx = 2*(5*2*8) + 1*(2*8) + 4*(8) + 3

使用multiply_but_last将所有剩余维度乘以最后一个(我看看如何使用constexpr变量模板函数实现,例如对于factorise,但我不明白是否可以使用std :: integer_sequence)。

我是可变参数模板操作和std :: integer_sequence的新手,我认为我错过了一些东西。是否有可能在没有开销的情况下获得线性索引计算(例如,如果操作是手写的话)?

非常感谢你的帮助。

2 个答案:

答案 0 :(得分:1)

以下可能有所帮助:

#include <array>
#include <cassert>
#include <iostream>

template <std::size_t, typename T> using alwaysT_t = T;

template<typename T, std::size_t ... Dims>
class MultiArray
{
public:
    const T& operator() (alwaysT_t<Dims, std::size_t>... indexes) const
    {
        return values[computeIndex(indexes...)];
    }
    T& operator() (alwaysT_t<Dims, std::size_t>... indexes)
    {
        return values[computeIndex(indexes...)];
    }

private:
    size_t computeIndex(alwaysT_t<Dims, std::size_t>... indexes_args) const
    {
        constexpr std::size_t dimensions[] = {Dims...};
        std::size_t indexes[] = {indexes_args...};

        size_t index = 0;
        size_t mul = 1;

        for (size_t i = 0; i != sizeof...(Dims); ++i) {
            assert(indexes[i] < dimensions[i]);
            index += indexes[i] * mul;
            mul *= dimensions[i];
        }
        assert(index < (Dims * ...));
        return index;
    }

private:
    std::array<T, (Dims * ...)> values;
};

Demo

我用折叠表达式(C ++ 17)替换了你的factorize

答案 1 :(得分:0)

我有一个非常简单的函数,可以将多维索引转换为一维索引。

#include <initializer_list>

template<typename ...Args>
inline constexpr size_t IDX(const Args... params) {
  constexpr size_t NDIMS = sizeof...(params) / 2 + 1;
  std::initializer_list<int> args{params...};
  auto ibegin = args.begin();
  auto sbegin = ibegin + NDIMS;
  size_t res = 0;
  for (int dim = 0; dim < NDIMS; ++dim) {
    size_t factor = dim > 0 ? sbegin[dim - 1] : 0;
    res = res * factor + ibegin[dim];
  }
  return res;
}
  

如果看到类似non-constant-expression cannot be narrowed from type 'int'之类的警告,则可能需要在编译器中添加“ -Wno-c ++ 11-narrowing”标志。

用法示例:

2D阵列

int array2D[rows*cols];
// Usually, you need to access the element at (i,j) like this:
int elem = array2D[i * cols + j]; // = array2D[i,j]
// Now, you can do it like this:
int elem = array2D[IDX(i,j,cols)]; // = array2D[i,j]

3D阵列

int array3D[rows*cols*depth];
// Usually, you need to access the element at (i,j,k) like this:
int elem = array3D[(i * cols + j) * depth + k]; // = array3D[i,j,k]
// Now, you can do it like this:
int elem = array3D[IDX(i,j,k,cols,depth)]; // = array3D[i,j,k]

ND阵列

// shapes = {s1,s2,...,sn}
T arrayND[s1*s2*...*sn]
// indices = {e1,e2,...,en}
T elem = arrayND[IDX(e1,e2,...,en,s2,...,sn)] // = arrayND[e1,e2,...,en]

请注意,传递给IDX(...)的形状参数从第二个形状开始,在本例中为s2

  

顺便说一句:此实现需要C ++ 14。