【问题标题】:fill static templated arrays with metaprogramming and variadic templates使用元编程和可变参数模板填充静态模板化数组
【发布时间】:2013-06-18 12:18:58
【问题描述】:

我知道有更简单的方法可以做到这一点,但是 我想在编译时初始化 从二维数组的展开索引到其一般格式的映射。

我想这样做而不需要实例化数组对象。

下面我从array[][]->array[] 定义地图。 现在我想知道如何做相反的事情:[] -> [][] 无需对所选映射方案进行硬编码。

我想这应该可以使用元编程和可变参数模板来实现。 但是几天前我第一次尝试使用它, 所以需要一段时间来适应;)

标题:

template <int dim>
class internal {
    static unsigned int table[dim][dim];
    static unsigned int x_comp[dim*dim];
    static unsigned int y_comp[dim*dim];
};

来源:

//1d case:

template <>
unsigned int
internal<1>::table[1][1]  = {{0}};

template <>
unsigned int
internal<1>::x_component[1] = {0};

template <>
unsigned int
internal<1>::y_component[1] = {0};

//2d case:

template<>
unsigned int
internal<2>::table[2][2] =
            {{0, 1},
             {2, 3}
            };

// here goes some metaprogramming tricks to initialize
// internal<2>::y_component[2*2] = ...
// internal<2>::x_component[2*2] = ... 
// based on mapping above, i.e. table[2][2];
// that is:
// x_table = { 0, 0, 1, 1 }
// y_table = { 0, 1, 0, 1 }
// 
// so that :
//
//  index == table[i][j]
//  i     == x_comp[index]
//  j     == y_comp[index]

EDIT1:

或者只是告诉我这是不可能的,我对所有内容都进行了硬编码或使用 整数除法以关联两个索引表示。

EDIT2: 我宁愿坚持任意数组的定义。 当然可以不用,如下面的答案中使用整数除法。

这些数组可以是任意的,例如:

template<>
unsigned int
internal<2>::table[2][2] =
            {{3, 0},
             {2, 1}
            };

【问题讨论】:

  • 您希望在internal&lt;2&gt;::x_component 等中包含哪些值?
  • @ArneMertz 我在最后修改了问题以使其清楚
  • 你能像std::arrayconstexpr一样使用C++11的特性吗?如果您只想拥有这些索引,则不需要整个数组。
  • @ArneMertz std::array 和 constexpr 很好。我确实在其他地方使用 constexpr

标签: c++ templates metaprogramming template-meta-programming


【解决方案1】:

使用数组:

给定一个具有从 0 到 dim^2-1 的唯一条目的表,您可以为给定表条目的 i 和 j 编写 constexpr 查找函数:

constexpr unsigned get_x_comp(unsigned index, unsigned i=0, unsigned j=0) 
{ return table[i][j] == index ? i : get_x_comp(index, ((j+1)%dim ? i : i+1), (j+1)%dim); }

constexpr unsigned get_y_comp(unsigned index, unsigned i=0, unsigned j=0) 
{ return table[i][j] == index ? j : get_y_comp(index, ((j+1)%dim ? i : i+1), (j+1)%dim); }

这些将递归调用自己,遍历表并寻找index。当找到给定索引并返回该索引的i/j 时,递归最终将结束。

结合 Jonathan 提到的 C++14 std::integer_sequence 来初始化数组:

template<unsigned... I>
constexpr auto make_x_comp(std::integer_sequence<unsigned, I...>) -> std::array<unsigned, sizeof...(I)> { return {get_x_comp(I)...}; }

使用元函数代替数组:

在某些情况下,甚至可能不需要数组。我假设您希望 table 包含从 0 到 dim^2-1 的连续索引。如果是这样的话,tablex_compy_comp 只是具有以下属性的简单编译时函数:

  • table(i,j) := i*dim + j
  • x_comp(index) := index / dim(整数除法)
  • y_comp(index) := index % dim

根据您是否有可用的 C++11 功能,实现会有所不同,但两次都没有数组。

注意:以下实现将假定table 中存储的数字从0 到dim^2-1 是连续的。如果不是,您必须为table 滚动您自己的适当函数并使用上述get_x_compget_y_comp 实现

C++11:

template <unsigned dim> //use unsigned to avoid negative numbers!
struct internal {
  static constexpr unsigned table(unsigned i, unsigned j) { return i*dim+j; }
  static constexpr unsigned x_comp(unsigned index) { return index/dim; }
  static constexpr unsigned y_comp(unsigned index) { return index%dim; }
};

您可以像普通函数一样在任何地方调用这些函数,尤其是在需要编译时常量的任何地方。示例:int a[internal&lt;5&gt;::table(2,4)];

C++03:

template <unsigned dim> //use unsigned to avoid negative numbers!
struct internal {
  template<unsigned i, unsigned j>
  struct table{ static const unsigned value = i*dim+j; };
  template<unsigned index>
  struct x_comp{ static const unsigned value = index/dim; };
  template<unsigned index>
  struct y_comp{ static const unsigned value = index%dim; };
};

使用这些元函数比在 C++11 中要笨拙一些,但可以像往常一样使用模板元函数。同上例:int a[internal&lt;5&gt;::table&lt;2,4&gt;::value];

注意:这一次您可以将(元)函数放在头文件中,因为它们不再是非整数静态成员变量。此外,您不需要将模板限制为小尺寸,因为对于小于 sqrt(numeric_limits&lt;unsigned&gt;::max()) 的尺寸,所有内容都将得到很好的计算。

【讨论】:

  • 感谢 Arne 的回答。很抱歉没有马上说我需要这些数组。否则它是真的,一个人可以通过整数除法轻松地做到没有数组。
  • @Denis 你可以使用 Jonathan 的方法来填充 table,我添加了一个测量值来使用 constexpr 函数填充 x_comp。查看我的编辑。
  • 不错。这个答案的第一部分很好,但其余部分假设table 中的连续数字,并且对答案的编辑清楚地表明情况可能并非如此(这使我的答案错误。)要处理一般情况,解决方案必须使用get_x_compget_y_comp 函数在table 中搜索值,我没有这样做。
【解决方案2】:

很抱歉,如果我没有直接(或根本没有)回答这个问题,但我真的不明白你在问什么。我认为您的意思是您想在编译时初始化一种将大小为 N x M 的数组表示为一维数组的方法?

我已包含允许您分配非方形尺寸的代码。我已经用“简单”的 C++ 构建了它,所以如果你只是进入模板,它并不难理解。

有可能做这样的事情吗?

template <typename T, typename std::size_t N, typename std::size_t M = 1>
class Array {
    T* data;
public:
    Array<T, N, M>() : data(new T[N * M]) {
        T temp = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                data[i * M + j] = temp++;
            }
        }
    }
    /* methods and stuff
}

其中 M 是列号,因此您可以这样使用:

int main(void) {

    Array<float, 10, 10> myArray;

    return 0;
}

记得在析构函数中调用delete

【讨论】:

  • 需要实例化此类的对象。这正是我不想做的。无论如何,谢谢您的回答
【解决方案3】:

编辑:当我写这篇文章时,我不明白填充x_compy_comp 的规则,现在我看到问题的一部分,这个答案并不真正相关,因为我错误地假设table 只包含连续的整数。无论如何,答案都留在这里,因为 Arne 的(更好的)答案是指它。


我会用 std::array 替换数组并使用 C++14 integer_sequence 实用程序:

template <int dim>
struct internal {
    static std::array<std::array<unsigned, dim>, dim> table;
    static std::array<unsigned, dim*dim> x_comp;
    static std::array<unsigned, dim*dim> y_comp;
};

template<unsigned Origin, unsigned... I>
constexpr std::array<unsigned, sizeof...(I)>
make_1d_array_impl(std::integer_sequence<unsigned, I...>)
{
    return { { I + Origin ... } };
}

template<int N>
constexpr std::array<unsigned, N*N>
make_1d_array()
{
    return make_1d_array_impl<0>(std::make_integer_sequence<unsigned, N*N>{});
}


template<unsigned... I>
constexpr std::array<std::array<unsigned, sizeof...(I)>, sizeof...(I)>
make_2d_array_impl(std::integer_sequence<unsigned, I...> seq)
{
    return { { make_1d_array_impl<I*sizeof...(I)>(seq)  ... } };
}

template<int N>
constexpr std::array<std::array<unsigned, N>, N>
make_2d_array()
{
    return make_2d_array_impl(std::make_integer_sequence<unsigned, N>{});
}

template<int dim>
std::array<std::array<unsigned, dim>, dim> internal<dim>::table = make_2d_array<dim>();

这会正确填充table 数组。我需要多考虑一下才能根据需要填充 x_compy_comp,但这是可行的。

您可以在 https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h 找到 integer_sequence 的 C++11 实现

【讨论】:

  • 我不太明白你是如何使用internal&lt;2&gt;::table[2][2]的定义的?
  • 我没有使用internal&lt;2&gt;,我展示了如何定义和填充internal&lt;N&gt;::table为任何N。我不明白你在 x 和 y 数组中想要什么值
  • 值在评论部分。它实际上是二维数组的两种表示之间的映射 - 索引对 (x,y) 和单个(展开)索引。这个internal&lt;2&gt;::table[2][2] 定义了pair->index;问题是如何填充 index->​​pair;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多