2

我的问题是指我之前针对一维数组提出的问题:

for 循环消除

有人可以帮我将索引技巧的使用扩展到多维数组,例如在这个例子中:

template<unsigned...> struct indices
{
};

template<unsigned M, unsigned... Is> struct indices_gen
  : indices_gen<M - 1, M - 1, Is...>
{
};

template<unsigned... Is> struct indices_gen<0, Is...> : indices<Is...>
{
};

template <typename T>
struct example
{
  template<typename ...U, typename
    = typename std::enable_if<all_of<std::is_same<U, T>...>::value>::type>
  example(U... args)
  {
    static_assert(3 * 2 == sizeof...(U),
      "wrong number of arguments in assignment");
    assign(indices_gen<M * N>(), args...);
  }

  template<size_type... Is, class... U>
  void assign(indices<Is...>, U... args)
  {
    [](...){}(((&array[0][0])[Is] = args)...);
  }

  T array[3][2];
};

int main()
{
  example<int> ex(1, 2, 3, 4, 5, 6);
  return 0;
}

目前我取决于要求,即数组是连续的,但我想array使用成对的索引进行分配,而不仅仅是一个索引(这样我就可以支持数组以外的类型,特别是覆盖的类型operator[])。如果我使用 2 个参数包进行赋值,我将只在索引 (0, 0)、(1, 1)、... 处进行赋值,当array不同(如示例中所示)。

4

1 回答 1

4

通过更改代码以访问数组,而不是索引生成,这可以变得更容易。您基本上想要的是一维到二维的访问映射。

通常,当人们根据 1D 数组†</sup> 实现 2D 数组时,他们需要相反的方式(2D 到 1D):

template<class T, unsigned M, unsigned N>
struct md_array{
  T elems[M * N]; // same layout as 'T elems[M][N];'
};

i那么从 2D 索引得到 1D 索引的公式(x,y)i == x * N + y。如果我们elems将上面的 1D 想象为 2D 数组(使用M == 2and N == 3),我们可以解释这一点:

 0, 1, 2, 3, 4, 5 // indices (i)
[a, b, c, d, e, f]
v // we want them as M (2) packs of N (3) elements
 0, 1, 2   0, 1, 2 // element indices (y)
[a, b, c] [d, e, f]
\___0___/ \___1___/ // pack indices (x)
v // fusing the packs back together, we can see that we have
  // a constant offset for the packs, which is the N (3) times x
0*3+0, 0*3+1, 0*3+2, 1*3+0, 1*3+1, 1*3+2
[ a,     b,     c,     d,     e,     f ]

因此,我们得到i == x * N + y。我们现在需要解决这个公式,不是 fori而是 forxy。对于x,这很容易(使用数学符号):

i = x * N + y | -y
i - y = x * N | *(1/N)
i - y
----- = x
  N

所以x == (i - y) / N。现在,可悲的是,我不知道如何y使用纯数学来解决这个问题,但我们不需要那个。查看元素索引,我们可以看到它们环绕N,并且可以使用模运算符轻松完成。因此,y == i % N.

现在我们可以实现一个方法,该方法采用线性索引i并从中返回已解决的元素(x, y)

template<unsigned I>
T& get(){ constexpr auto y = I % 3; return array[(I-y)/3][y]; }

并概括为:

template<unsigned I>
T& get(){ constexpr auto y = I % N, x = (I-y)/N; return array[x][y]; }

用于constexpr确保所有计算在编译时完成。现在你可以简单地写assign如下:

template<unsigned... Is, class... U>
void assign(indices<Is...>, U... args)
{
  [](...){}((get<Is>() = args)...);
}

QED(现场示例。


† 现在,您可以通过将 2D 数组实际实现为 1D 数组来简化此操作。:) 这样,您可以直接使用(array[Is] = args)...并在其他情况下使用执行映射的简单访问函数:

T& get(unsigned x, unsigned y){ return array[x * N + y]; }

活生生的例子。

于 2012-10-15T09:50:03.413 回答