我正在尝试初始化多维数组,尽管有可能在启动时填充一次该数组,但我确实希望该数组为constexpr
,所以我想知道是否有一种方法可以获取编译器会为我执行此操作,特别是因为我可以提供一个constexpr函数,该函数接受每个索引的参数并返回数组应位于索引处的值。
例如:
constexpr bool test_values[64][64][64][64] = {
... // magic goes here
};
我有一个函数constexpr bool f(int,int,int,int)
,它告诉我每个元素应该是什么。我宁愿通过数组访问条目,因为执行数组查找比调用非常量值的f()更快。
我发现与运行时初始化数组有关的其他大多数问题都使用std :: array而不是C数组,而且我找不到的是多维的。我曾尝试将多维数组展开为一维数组,并使用std :: array方法,例如我在this question的答案之一中找到的方法,但是我发现gcc 9.1生成的结果代码仍然在启动时填充一次数组,而不是由编译器直接生成数组。
我是否可以做些什么来使编译器填充此类数组,还是我不得不将test_values
设置为实际上非constexpr
并在运行时初始化一次? >
编辑:
为澄清起见,我本来就不反对使用std :: array而不是内置的C样式数组,但是我不认为std :: arrays对多个维度特别友好,并且使用一维数组混淆了我的程序需要做的事情(坦率地说,我愿意将其实现为一维std :: array,但是多维数组比具有相同大小的一维数组更容易混淆被手动解开,这就是为什么我用多维C数组描述它的原因。
答案 0 :(得分:0)
C ++不允许从函数返回文字数组(请参见https://stackoverflow.com/a/4264495),但是正如其他人所述,返回std::array<>
会导致功能上相同的内存内容。
AFAICT以下方法在gcc,msvc和clang中生成预烘焙的常量(.rodata节)。我归纳为3维。不幸的是,它还会使任何大小合适的数组(如64x64x64)上的编译器崩溃,并出现编译器错误virtual memory exhausted: Cannot allocate memory
。所以我认为这不太实用。 [FWIW,32x32x32确实成功了]
基本方法是为每个包含数组索引0,1,2,...,NumDim-1
的维创建一个parameter pack,并为较大的维创建固定的索引。然后以与std::experimental::make_array
类似的方式返回一个std::array
,其内容是应用于Value(x, y, z)
函数的索引。
constexpr bool Value(size_t x, size_t y, size_t z)
{
return (bool)((x ^ y ^ z) & 1);
}
namespace ValueArrayDetail {
template <size_t NumX, size_t X>
struct IteratorX
{
template <class... Xs>
static constexpr std::array<bool, NumX> MakeXs(size_t z, size_t y, Xs... xs)
{
return IteratorX<NumX, X - 1>::template MakeXs(z, y, X - 1, xs...);
}
};
template <size_t NumX>
struct IteratorX<NumX, 0>
{
template <class... Xs>
static constexpr std::array<bool, NumX> MakeXs(size_t z, size_t y, Xs... xs)
{
return { Value(xs, y, z)... };
}
};
template <size_t NumX, size_t NumY, size_t Y>
struct IteratorY
{
template <class... Ys>
static constexpr std::array<std::array<bool, NumX>, NumY> MakeYs(size_t z, Ys... ys)
{
return IteratorY<NumX, NumY, Y - 1>::template MakeYs(z, Y - 1, ys...);
}
};
template <size_t NumX, size_t NumY>
struct IteratorY<NumX, NumY, 0>
{
template <class... Ys>
static constexpr std::array<std::array<bool, NumX>, NumY> MakeYs(size_t z, Ys... ys)
{
return { IteratorX<NumX, NumX>::template MakeXs(z, ys)... };
}
};
template <size_t NumX, size_t NumY, size_t NumZ, size_t Z>
struct IteratorZ
{
template <class ... Zs >
static constexpr std::array<std::array<std::array<bool, NumX>, NumY>, NumZ> MakeZs(Zs... zs)
{
return IteratorZ<NumX, NumY, NumZ, Z - 1>::template MakeZs(Z - 1, zs...);
}
};
template <size_t NumX, size_t NumY, size_t NumZ>
struct IteratorZ<NumX, NumY, NumZ, 0>
{
template <class... Zs>
static constexpr std::array<std::array<std::array<bool, NumX>, NumY>, NumZ> MakeZs(Zs... zs)
{
return { IteratorY<NumX, NumY, NumY>::template MakeYs(zs)... };
}
};
template <size_t NumX, size_t NumY, size_t NumZ>
static constexpr std::array<std::array<std::array<bool, NumX>, NumY>, NumZ> MakeValues()
{
return IteratorZ<NumX, NumY, NumZ, NumZ>::template MakeZs();
}
}
auto constexpr test_values = ValueArrayDetail::MakeValues<3, 4, 5>();
您可以使用文字常量初始化test_values,与使用普通const
数组的方式相同。对每个尺寸使用嵌套括号。下面的示例有点懒惰地编写,每行64个只有4个值,但是在输出中清楚地显示了每个未明确指定的数据的默认值为零。
输入:
constexpr bool test_values[64][64][64][64] = {
{
{
{true, false, false, true},
{false, true, false, false},
{true, true, true, true},
},
{
{1, 0, 0, 1},
{1, 1, 1, 0},
{0, 0, 1, 1},
},
}
};
输出(x86-64 gcc 9.1):
test_values:
.byte 1 <-- test_values[0][0][0][0]
.byte 0
.byte 0
.byte 1
.zero 60 <-- test_values[0][0][0][4 .. 63]
.byte 0 <-- test_values[0][0][1][0]
.byte 1
.zero 62 <-- test_values[0][0][1][2 .. 63]
.byte 1 <-- test_values[0][0][2][0]
.byte 1
.byte 1
.byte 1
.zero 60 <-- test_values[0][0][2][2 .. 63]
.zero 3904
.byte 1 <-- test_values[0][1][0][0]
.byte 0
.byte 0
.byte 1
.zero 60
.byte 1
.byte 1
.byte 1
.zero 61
.byte 0
.byte 0
.byte 1
.byte 1
.zero 60
.zero 3904
.zero 253952
.zero 16515072
答案 1 :(得分:0)
C数组不可复制,因此使用函数实际上是不可能的,但是使用std::array
,您可能会创建constexpr函数(尽管C ++ 11受到更多限制)
constexpr auto generate()
{
std::array<std::array<std::array<std::array<bool, 64>, 64>, 64>, 64> res{};
for (int a = 0; a != 64; ++a) {
for (int b = 0; b != 64; ++b) {
for (int c = 0; c != 64; ++c) {
for (int d = 0; d != 64; ++d) {
res[a][b][c][d] = f(a, b, c, d);
}
}
}
}
return res;
}
constexpr auto test_values = generate();
如果您确实需要C数组,则可以将其包装在结构中并使用类似的代码。