如何在 C++17 中生成内联 constexpr 普通数组数组
How to generate an inline constexpr plain array of array in c++17
问题
我正在用 c++17 编写一个软件,性能对它来说绝对至关重要。我想在一些关键函数中提供数组中的常量本身。重要的是,这两个数组都可以通过这种(或类似)方式的整数值访问:
int main()
{
for (int i = 0; i < size_of_A ; i++)
{
for (int j = 0; j < size_of_B_in_A(i); j++)
{
std::cout << A[i][j];
}
}
}
这就是我们想要创建的数组类型,假设有一些函数 int f(a, b)
A
{
// B1
{
f(1, 1),
f(1, 2),
f(1, 3),
...
f(1, large number)
},
// B2
{
f(2, 1),
...
f(2, some other large number)
},
... etc
}
扭曲
每个内部数组可能有不同的大小,我们将存储在其他地方,我们必须在编译时找到大小。我宁愿不使用 std::vector 因为它们被假定为
稍慢
。
另外我想 std::vector 会存储在堆上,这在我的特定情况下会是一个性能问题。
此外,
std::vector 不能用作 "inline constexpr"
这是必要的,因为我希望这些数组中的大量值永远不会改变。我愿意每次都重新编译所有这些值,但不会按照策略将它们保存在外部文件中,因为我要遵循严格的编码风格。
我试过的
花括号初始化器
// A.hh
#pragma once
#include <iostream>
void test1();
void test2();
inline constexpr int B1[1] = {1};
inline constexpr int B2[2] = {2, 3};
inline constexpr int B3[3] = {4, 5, 6};
inline constexpr const int *A[3] = {B1, B2, B3};
// main.cc
#include "A.hh"
int main()
{
std::cout << "values : ";
for (int i = 0; i < 3; i++)
{
for (int j = 0; j <= i; j++)
{
std::cout << A[i][j];
}
}
std::cout << "\n\naddress test : \n";
std::cout << &A << '\n';
test1();
test2();
}
// somewhere.cc
#include "A.hh"
void test1()
{
std::cout << &A << '\n';
}
// elsewhere.cc
#include "A.hh"
void test2()
{
std::cout << &A << '\n';
}
打印:
./a.out
values : 123456
address test :
0x56180505cd70
0x56180505cd70
0x56180505cd70
因此A在main.cc、somewhere.cc和elsewhere.cc中没有被复制,这很好。我想更进一步,能够创造出巨大的价值。
带 constexpr 的结构
使用找到的提示
这里
,我这样做是为了能够在数组构造期间执行操作。
// B.hh
#pragma once
#include <iostream>
template <int N>
struct X
{
int arr[N];
constexpr X(): arr()
{
for (int i = 0; i < N; i++)
{
arr[i] = i % 3;
}
}
};
inline constexpr auto A = X<500>();
// main.cc
#include "B.hh"
int main()
{
for (int i = 0; i < 500; i++)
{
std::cout << A.arr[i];
}
}
毫无疑问地打印出来
012012 (etc)...
终于是数组数组
这就是我被困的地方
#pragma once
#include <iostream>
template <int N>
struct sub_array
{
int arr[N];
constexpr sub_array() : arr()
{
for (int i = 0; i < N; i++)
{
arr[i] = i;
}
}
};
struct array
{
sub_array</*what here ?*/> arr[100];
constexpr array() : arr()
{
for (int i = 0; i < 100; i++)
{
int size = i * 2; // a very large number
// the value of 'size' is not usable in a constant expression
//
// I see why it is, but I can't think of any other way
arr[i] = sub_array<size>;
}
}
};
inline constexpr array A = array();
如何构建这样的数组?
感谢您的时间和考虑。
只需使用std::array<std::span<int>, N>
,这是一个由不同大小的跨度组成的固定大小的数组。要生成它,请使用 std::index_sequence
Header:
constexpr std::size_t size_of_A = 500;
extern const std::array<const std::span<const int>, size_of_A>& A;
实施:
constexpr std::size_t size_of_B_in_A(std::size_t i) { return i%10+1;}
constexpr int f(std::size_t i, std::size_t j) {return static_cast<int>(i%(j+1));}
template <int I, int N>
struct B
{
std::array<int,N> arr;
explicit constexpr B()
{
for (int j = 0; j < N; j++)
arr[j] = f(I, j);
}
constexpr operator const std::span<const int>() const {return {arr};}
};
template<class index_sequence>
class BGen;
template<std::size_t... I>
struct BGen<std::integer_sequence<std::size_t,I...>> {
static constexpr std::tuple<B<I, size_of_B_in_A(I)>...> bs{};
static constexpr std::array<const std::span<const int>, sizeof...(I)> A {std::get<I>(bs)...};
};
const std::array<const std::span<const int>, size_of_A>& A
= BGen<decltype(std::make_index_sequence<size_of_A>{})>::A;
用法:
int main()
{
for (unsigned i = 0; i < A.size() ; i++)
{
for (unsigned j = 0; j < A[i].size(); j++)
{
std::cout << A[i][j];
}
}
}
http://coliru.stacked-crooked.com/a/d68b0e9fd6142f86
但是,退一步说:这个解决方案不是解决这个问题的正常方法。因为都是constexpr
,所以这都是数据,不是代码。因此,最高效的解决方案是 两个 程序。一个生成数据并将其保存到程序附带的文件中(内部?)。然后你的程序简单地将文件映射到内存,并直接使用数据。
这是一种实现 constexpr 交错数组的方法,它可以在没有中间体的情况下进行初始化。它确实需要将行大小列为模板参数,但也有一些方法可以使这更容易,具体取决于在编译时如何知道行大小。
#include <tuple>
#include <array>
#include <utility>
template <std::size_t ...Sizes>
struct jagged_array
{
const std::tuple<std::array<int,Sizes>...> data;
static constexpr std::size_t num_rows = sizeof...(Sizes);
static constexpr std::size_t length[num_rows]{Sizes...};
int const* const row_ptr[num_rows];
template <std::size_t ...I>
constexpr jagged_array(std::index_sequence<I...>,
const std::array<int, Sizes>& ...arrs)
: data{arrs...}, row_ptr{&std::get<I>(data)[0]...} {}
constexpr jagged_array(const std::array<int, Sizes>& ...arrs)
: jagged_array(std::make_index_sequence<num_rows>{}, arrs...)
{}
constexpr int const* operator[](std::size_t idx) const
{ return row_ptr[idx]; }
};
inline constexpr jagged_array<2,4> jarr = {{2,3}, {4,5,6,7}};
问题
我正在用 c++17 编写一个软件,性能对它来说绝对至关重要。我想在一些关键函数中提供数组中的常量本身。重要的是,这两个数组都可以通过这种(或类似)方式的整数值访问:int main()
{
for (int i = 0; i < size_of_A ; i++)
{
for (int j = 0; j < size_of_B_in_A(i); j++)
{
std::cout << A[i][j];
}
}
}
这就是我们想要创建的数组类型,假设有一些函数 int f(a, b)
A
{
// B1
{
f(1, 1),
f(1, 2),
f(1, 3),
...
f(1, large number)
},
// B2
{
f(2, 1),
...
f(2, some other large number)
},
... etc
}
扭曲
每个内部数组可能有不同的大小,我们将存储在其他地方,我们必须在编译时找到大小。我宁愿不使用 std::vector 因为它们被假定为 稍慢 。 另外我想 std::vector 会存储在堆上,这在我的特定情况下会是一个性能问题。 此外, std::vector 不能用作 "inline constexpr" 这是必要的,因为我希望这些数组中的大量值永远不会改变。我愿意每次都重新编译所有这些值,但不会按照策略将它们保存在外部文件中,因为我要遵循严格的编码风格。我试过的
花括号初始化器
// A.hh
#pragma once
#include <iostream>
void test1();
void test2();
inline constexpr int B1[1] = {1};
inline constexpr int B2[2] = {2, 3};
inline constexpr int B3[3] = {4, 5, 6};
inline constexpr const int *A[3] = {B1, B2, B3};
// main.cc
#include "A.hh"
int main()
{
std::cout << "values : ";
for (int i = 0; i < 3; i++)
{
for (int j = 0; j <= i; j++)
{
std::cout << A[i][j];
}
}
std::cout << "\n\naddress test : \n";
std::cout << &A << '\n';
test1();
test2();
}
// somewhere.cc
#include "A.hh"
void test1()
{
std::cout << &A << '\n';
}
// elsewhere.cc
#include "A.hh"
void test2()
{
std::cout << &A << '\n';
}
打印:
./a.out
values : 123456
address test :
0x56180505cd70
0x56180505cd70
0x56180505cd70
因此A在main.cc、somewhere.cc和elsewhere.cc中没有被复制,这很好。我想更进一步,能够创造出巨大的价值。
带 constexpr 的结构
使用找到的提示 这里 ,我这样做是为了能够在数组构造期间执行操作。// B.hh
#pragma once
#include <iostream>
template <int N>
struct X
{
int arr[N];
constexpr X(): arr()
{
for (int i = 0; i < N; i++)
{
arr[i] = i % 3;
}
}
};
inline constexpr auto A = X<500>();
// main.cc
#include "B.hh"
int main()
{
for (int i = 0; i < 500; i++)
{
std::cout << A.arr[i];
}
}
毫无疑问地打印出来
012012 (etc)...
终于是数组数组
这就是我被困的地方#pragma once
#include <iostream>
template <int N>
struct sub_array
{
int arr[N];
constexpr sub_array() : arr()
{
for (int i = 0; i < N; i++)
{
arr[i] = i;
}
}
};
struct array
{
sub_array</*what here ?*/> arr[100];
constexpr array() : arr()
{
for (int i = 0; i < 100; i++)
{
int size = i * 2; // a very large number
// the value of 'size' is not usable in a constant expression
//
// I see why it is, but I can't think of any other way
arr[i] = sub_array<size>;
}
}
};
inline constexpr array A = array();
如何构建这样的数组?
感谢您的时间和考虑。
只需使用std::array<std::span<int>, N>
,这是一个由不同大小的跨度组成的固定大小的数组。要生成它,请使用 std::index_sequence
Header:
constexpr std::size_t size_of_A = 500;
extern const std::array<const std::span<const int>, size_of_A>& A;
实施:
constexpr std::size_t size_of_B_in_A(std::size_t i) { return i%10+1;}
constexpr int f(std::size_t i, std::size_t j) {return static_cast<int>(i%(j+1));}
template <int I, int N>
struct B
{
std::array<int,N> arr;
explicit constexpr B()
{
for (int j = 0; j < N; j++)
arr[j] = f(I, j);
}
constexpr operator const std::span<const int>() const {return {arr};}
};
template<class index_sequence>
class BGen;
template<std::size_t... I>
struct BGen<std::integer_sequence<std::size_t,I...>> {
static constexpr std::tuple<B<I, size_of_B_in_A(I)>...> bs{};
static constexpr std::array<const std::span<const int>, sizeof...(I)> A {std::get<I>(bs)...};
};
const std::array<const std::span<const int>, size_of_A>& A
= BGen<decltype(std::make_index_sequence<size_of_A>{})>::A;
用法:
int main()
{
for (unsigned i = 0; i < A.size() ; i++)
{
for (unsigned j = 0; j < A[i].size(); j++)
{
std::cout << A[i][j];
}
}
}
http://coliru.stacked-crooked.com/a/d68b0e9fd6142f86
但是,退一步说:这个解决方案不是解决这个问题的正常方法。因为都是constexpr
,所以这都是数据,不是代码。因此,最高效的解决方案是 两个 程序。一个生成数据并将其保存到程序附带的文件中(内部?)。然后你的程序简单地将文件映射到内存,并直接使用数据。
这是一种实现 constexpr 交错数组的方法,它可以在没有中间体的情况下进行初始化。它确实需要将行大小列为模板参数,但也有一些方法可以使这更容易,具体取决于在编译时如何知道行大小。
#include <tuple>
#include <array>
#include <utility>
template <std::size_t ...Sizes>
struct jagged_array
{
const std::tuple<std::array<int,Sizes>...> data;
static constexpr std::size_t num_rows = sizeof...(Sizes);
static constexpr std::size_t length[num_rows]{Sizes...};
int const* const row_ptr[num_rows];
template <std::size_t ...I>
constexpr jagged_array(std::index_sequence<I...>,
const std::array<int, Sizes>& ...arrs)
: data{arrs...}, row_ptr{&std::get<I>(data)[0]...} {}
constexpr jagged_array(const std::array<int, Sizes>& ...arrs)
: jagged_array(std::make_index_sequence<num_rows>{}, arrs...)
{}
constexpr int const* operator[](std::size_t idx) const
{ return row_ptr[idx]; }
};
inline constexpr jagged_array<2,4> jarr = {{2,3}, {4,5,6,7}};