【问题标题】:multidimensional array initialization using indices trick使用索引技巧进行多维数组初始化
【发布时间】:2012-10-23 19:18:52
【问题描述】:

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

for loop elimination

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

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 不同(如示例中所示)。

【问题讨论】:

    标签: c++ templates c++11


    【解决方案1】:

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

    通常,当人们根据一维数组实现二维数组时,他们需要相反的方式(2D 到 1D)

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

    从二维索引(x,y) 得到一维索引i 的公式是i == x * N + y。如果我们将上面的一维 elems 想象成一个二维数组(使用 M == 2N == 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。我们现在需要解决这个公式,不是i,而是xy。对于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)...);
    }
    

    Q.E.D. (Live example.)


    † 现在,您可以通过将二维数组实际实现为一维数组来简化此操作。 :) 这样,您可以直接使用(array[Is] = args)...,而对于其他情况,可以使用简单的访问函数来进行映射:

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

    Live example.

    【讨论】:

    • 但我认为更好的是笛卡尔有序对的生成器。
    猜你喜欢
    • 2015-07-06
    • 2018-12-29
    • 2015-08-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-12
    相关资源
    最近更新 更多