正在生成编译时数组结构(c++17)
Generating compile-time array structure (c++17)
我有以下C++17代码,用于生成数组的编译时元组,其中零数组只是为了示例,在我的实现中,它们将是满的(使用-std=C++1z-fconcepts编译)。
#include <array>
#include <tuple>
#include <cmath>
template <std::size_t nrA, std::size_t ncA, std::size_t nrB, std::size_t ncB,
typename number=double>
constexpr auto operator *(const std::array<std::array<number,ncA>,nrA> & A,
const std::array<std::array<number,ncB>,nrB> & B)
{
std::array<std::array<number,ncB>,nrA> res{} ;
for (auto k=0u;k<ncB;++k)
for (auto i=0u;i<nrA;++i)
for (auto j=0u;j<nrB;++j)
res[i][k] += A[i][j]*B[j][k];
return res ;
}
constexpr auto logg2(const auto N)
{
auto res = 0;
auto n = N;
while (n != 0)
{
n /= 2;
++res;
}
return res;
}
template <std::size_t N,typename number=double>
constexpr auto create_R()
{
return std::array<std::array<double,2*N>,N>{};
}
template <std::size_t N,typename number=double>
constexpr auto create_RT()
{
return std::array<std::array<double,N>,2*N>{};
}
template <std::size_t N,std::size_t ...Is>
constexpr auto make_impl(const std::index_sequence<Is...>)
{
return std::make_tuple(std::make_tuple(create_R<(N >> Is)>()...),
std::make_tuple(create_RT<(N >> Is)>()...));
}
template <std::size_t N,typename number=double>
constexpr auto make()
{
return make_impl<N/2>(std::make_index_sequence<logg2(N/2) - 1>());
}
int main(int argc, char *argv[])
{
const auto n = 4u;
const auto A = std::array<std::array<double,2*n>,2*n>{};
const auto [R,RT] = make<2*n>();
}
我想将make<>()
修改为make<>(A)
,并返回一个结构化绑定[R,RT,As]
,其中As
是一个元组,其中包含以下数组
A,
std::get<0>(R)*A*std::get<0>(RT),
std::get<1>(R)*std::get<0>(R)*A*std::get<0>(RT)*std::get<1>(RT)
...
我已经尝试了一段时间,但没有找到解决方案。
有什么想法吗?
编辑1
根据@MaxLanghof的要求,以下打印矩阵:
template <std::size_t nr, std::size_t nc, typename number=double>
constexpr auto print(const std::array<std::array<number,nc>,nr> & A)
{
for (auto i=0u;i<nr;++i)
{
for (auto j=0u;j<nc;++j)
std::cout << std::right << std::setw(12) << A[i][j];
std::cout << std::endl ;
}
std::cout << std::endl ;
}
并将以下行添加到main()
print(A);
print(std::get<0>(R)*A*std::get<0>(RT));
print(std::get<1>(R)*std::get<0>(R)*A*std::get<0>(RT)*std::get<1>(RT));
获得以下输出
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0
0 0
解决方案包括为As
元组的每个条目创建另一个索引序列,然后使用它来折叠表示乘法。为了可读性,我随意地将std::array<std::array<T, Cols>, Rows>
封装在一个类型中(这也是必要的,见下文)。对makeMul
的每次调用都会产生一个As
元组元素(原始A
是单独添加的)。
template <std::size_t Rows, std::size_t Cols, typename T = double>
struct Mat2D : std::array<std::array<T, Cols>, Rows> {};
template <class T_Rs, std::size_t... Is>
constexpr auto mult_lhs(T_Rs Rs, std::index_sequence<Is...>) {
return (std::get<sizeof...(Is) - Is - 1>(Rs) * ...);
}
template <class T_RTs, std::size_t... Is>
constexpr auto mult_rhs(T_RTs RTs, std::index_sequence<Is...>) {
return (std::get<Is>(RTs) * ...);
}
template <class T_A, class T_Rs, class T_RTs, std::size_t... Is>
constexpr auto makeMul_impl(T_A A, T_Rs Rs, T_RTs RTs,
std::index_sequence<Is...> is) {
return mult_lhs(Rs, is) * A * mult_rhs(RTs, is);
}
template <std::size_t Index, class T_A, class T_Rs, class T_RTs>
constexpr auto makeMul(T_A A, T_Rs Rs, T_RTs RTs) {
return makeMul_impl(A, Rs, RTs, std::make_index_sequence<Index + 1>());
}
template <std::size_t N, std::size_t... Is, typename T = double>
constexpr auto make_impl(const Mat2D<2 * N, 2 * N, T>& A,
std::index_sequence<Is...>) {
auto Rs = std::make_tuple(create_R<(N >> Is)>()...);
auto RTs = std::make_tuple(create_RT<(N >> Is)>()...);
auto As = std::make_tuple(A, makeMul<Is>(A, Rs, RTs)...);
return std::make_tuple(Rs, RTs, As);
}
template <std::size_t N, typename T = double>
constexpr auto make(const Mat2D<N, N, T>& A) {
return make_impl<N / 2>(A, std::make_index_sequence<logg2(N / 2) - 1>());
}
int main(int argc, char* argv[]) {
const auto n = 4u;
const auto A = Mat2D<2 * n, 2 * n, double>{};
const auto [Rs, RTs, As] = make(A);
}
演示
需要注意的是,std
类型的重载运算符在这里是一个问题,至少在clang
(更严格地遵循标准)中是这样:一旦您尝试在模板中使用重载的operator*
,它不会被找到,因为ADL在namespace std
中查找它(我最初将Mat2D
作为别名,而不是继承内容的struct
)-并且不允许向namespace std
添加内容(除了一些特定的自定义点)。至少我是这样理解这个错误的。
从std
类型继承总体来说相当糟糕,但我认为您的矩阵实际上是一个用户定义的类型,因此这些都无关紧要。
最后,我强烈建议您为元组类型提供实际名称。当你有元组的元组(数组的数组)时,每个读者都必须投入大量的时间,即使是掌握代码。例如,如果您将每个R
和RT
分组到一个结构中,就会有所帮助:
template<std::size_t N, typename T = double>
struct R_RT {
Mat2D<N, 2 * N, T> R;
Mat2D<2 * N, N, T> RT;
};
和/或具有例如
template<class TupleOfR, class TupleOfRT, class TupleOfAs>
struct Rs_RTs_As {
TupleOfR Rs;
TupleOfRT RTs;
TupleOfAs As;
};
即使这在技术上允许任何三种类型,它仍然记录了您应该在其中找到的内容,并且通过概念,您实际上可以适当地约束所有内容(包括"As
将比Rs
和RTs
多出一个元素"之类的有趣内容,这可能需要大多数读者一段时间才能从纯元组代码中实现)。
- 如何循环打印顶点结构
- 通过方法访问结构
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 使用不带参数的函数访问结构元素
- 预处理器:插入结构名称中的前一个行号
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 孤立代码块在结构中引发异常
- 有什么方法可以遍历结构吗
- 如何在c++17中制作一个模板包装器/装饰器
- 如何在 C# 中映射双 C 结构指针?
- c++ 17 中结构自动定义构造函数的规则是什么?
- 通过类比理解 C++17 中的结构化绑定
- 正在生成编译时数组结构(c++17)
- 为什么结构variant_size;在 C++17 中未定义
- 无法编译 C++ 17 个结构化绑定
- 使用C 17可以检测结构/类是否具有任何基础
- JNI jni/cyberlevel9.c:17:31:错误:请求成员'NewDirectByteBuffer'不是结构或工会
- 为什么c++ 17的结构化绑定不使用{}?
- 模板结构在c++ 17中使用转发引用时需要std::衰变吗?
- 返回 C++17 可变参数模板'construction deduction guide'的可变参数聚合(结构)和语法