Решение включает в себя создание другой индексной последовательности для каждой записи в кортеже 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
»,что, вероятно, понадобится большинству читателей, чтобы понять из чистого кода кортежа).