2

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

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

下面我从 定义地图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]

编辑1:

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

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

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

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

3 回答 3

3

使用数组:

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

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

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

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_comp只是y_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<5>::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<5>::table<2,4>::value];

注意:这次您可以将(元)函数放在头文件中,因为它们不再是非整数静态成员变量。此外,您不需要将模板限制为小尺寸,因为对于小于sqrt(numeric_limits<unsigned>::max()).

于 2013-06-18T12:51:39.420 回答
1

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


std::array我会用 C++14integer_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根据您的需要,但它是可行的。

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

于 2013-06-18T12:46:51.613 回答
1

如果我没有直接(或根本没有)回答这个问题,我很抱歉,但我真的不明白你在问什么。我认为您的意思是您想在编译时初始化一种将大小为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析构函数。

于 2013-06-18T12:47:49.010 回答