我的问题提到了我之前问过的一维问题:
有人可以帮助我将指数技巧的使用扩展到多维数组,例如在这个例子中:
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
不同(如示例所示)。
答案 0 :(得分:4)
通过更改访问数组的代码而不是索引生成,可以更轻松地做到这一点。您基本上想要的是1D到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
获取一维索引(x,y)
的公式为i == x * N + y
。如果我们将上面的1D elems
想象为2D数组(使用M == 2
和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
。我们现在需要为i
而不是x
和y
解决此公式。对于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.)
†现在,您可以通过将2D数组实际实现为一维数组来使自己更容易。 :)这样,您可以直接使用(array[Is] = args)...
,而对于其他情况,可以使用执行映射的简单访问函数:
T& get(unsigned x, unsigned y){ return array[x * N + y]; }