C++ 嵌套参数包扩展
请参阅下面的代码片段(矩阵乘法的实现)。C++ 嵌套参数包扩展,c++,c++17,C++,C++17,请参阅下面的代码片段(矩阵乘法的实现)。是否可以使用类似于{((a[r][k]*b[k][c])+…)}的东西来简化它们 #include <array> #include <utility> template<typename T, size_t R, size_t C> using Matrix = std::array<std::array<T, C>, R>; template<typename A, typename
是否可以使用类似于
{((a[r][k]*b[k][c])+…)}
的东西来简化它们
#include <array>
#include <utility>
template<typename T, size_t R, size_t C>
using Matrix = std::array<std::array<T, C>, R>;
template<typename A, typename B>
using mul_el_t = decltype(std::declval<A>()[0][0] * std::declval<B>()[0][0]);
使用Apple LLVM 9.1.0版(clang-902.0.39.1)
编译失败,原因是:
[ 50%] Building CXX object CMakeFiles/main.cpp.o
main.cpp:38:51: error: pack expansion does not contain any unexpanded parameter packs
return {{((a[R1][C1_R2] * b[C1_R2][C2]) + ...)...}...};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
我认为失败是意料之中的,因为编译器不知道在每个扩展块中扩展哪个包(R1
、C2
或C1\ur2
)。
在这种情况下,我如何提示编译器(注意,我可以使用任何编译器)?根据文档,嵌套包扩展可以被视为一个迭代过程,从最里面的包扩展开始[3点]。每个包扩展都会扩展该包扩展包含的子表达式中的所有参数包 因此,在第一步之后,
{((a[R1][C1\ur2]*b[C1\ur2][C2])+…}成为
{(a[0][0]*b[0][0]+a[1][1]*b[1][1])}
(R1/C2/C1\ur2
是索引序列。所以接下来的两个包扩展没有什么可扩展的
原始解决方案
可以同时将每个参数包移动到所需的子表达式,将其携带的实际值保留在所需的位置。我们可以使用FP的模拟<代码>让。。。在
表达式中:
auto let = [](auto a, auto f) { return f(a); };
因此,最初的表达变成:
{let(R1, [&](auto r1) {
return std::array<T, sizeof...(C2)>{let(C2, [&](auto c2) {
return ((a[r1][C1_R2] * b[C1_R2][c2]) + ...);
})...};
})...};
其中ctor
是
template<typename H>
auto ctor()
{
return [](auto... xs) { return H{xs...}; };
}
使用实用程序:
template<typename F>
auto curry(F f)
{
return [=](auto... a) {
return [=](auto... b) { return f(a..., b...); };
};
};
template<typename F, typename G>
auto compose(F f, G g)
{
return [=](auto... xs) {
return f(g(xs...));
};
};
和foldr
,作为家庭作业
汇编说明
所有解决方案在生成相同二进制文件的意义上都是等效的。gcc无法做到这一点,这是一个非常古老的错误。这个错误仍然存在于gcc8中:我可以使用任何其他编译器。问题是我不能想出好的代码来使用。请参考Q更新。
[ 50%] Building CXX object CMakeFiles/main.cpp.o
main.cpp:38:51: error: pack expansion does not contain any unexpanded parameter packs
return {{((a[R1][C1_R2] * b[C1_R2][C2]) + ...)...}...};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
auto let = [](auto a, auto f) { return f(a); };
{let(R1, [&](auto r1) {
return std::array<T, sizeof...(C2)>{let(C2, [&](auto c2) {
return ((a[r1][C1_R2] * b[C1_R2][c2]) + ...);
})...};
})...};
template<typename H, typename F, typename T, T... I>
decltype(auto) repack(std::integer_sequence<T, I...>, H h, F f)
{
return h(f(std::integral_constant<T, I>{})...);
}
template<typename T, size_t R1, size_t C1_R2, size_t C2>
Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1_R2> &a, const Matrix<T, C1_R2, C2> &b)
{
std::make_index_sequence<R1> r1{};
std::make_index_sequence<C2> c2{};
std::make_index_sequence<C1_R2> c1_r2{};
return repack(r1, ctor<Matrix<T, R1, C2>>(), [&](auto r1) {
return repack(c2, ctor<std::array<T, C2>>(), [&](auto c2) {
return repack(c1_r2, sum, [&](auto c1_r2) {
return a[r1][c1_r2] * b[c1_r2][c2];
});
});
});
}
template<typename H>
auto ctor()
{
return [](auto... xs) { return H{xs...}; };
}
template<typename T, size_t R1, size_t C1_R2, size_t C2>
Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1_R2> &a, const Matrix<T, C1_R2, C2> &b)
{
auto item = [&](auto r1, auto c2, auto c1_r2) { return a[r1][c1_r2] * b[c1_r2][c2]; };
auto curried_repack = curry(POLY(repack));
auto m = curried_repack(std::make_index_sequence<R1>{}, ctor<Matrix<T, R1, C2>>());
auto r = curried_repack(std::make_index_sequence<C2>{}, ctor<std::array<T, C2>>());
auto e = curried_repack(std::make_index_sequence<C1_R2>{}, sum);
auto op = [](auto w, auto f) {
return compose(w, curry(f));
};
return foldr(op, m, r, e, item)();
}
template<typename F>
auto curry(F f)
{
return [=](auto... a) {
return [=](auto... b) { return f(a..., b...); };
};
};
template<typename F, typename G>
auto compose(F f, G g)
{
return [=](auto... xs) {
return f(g(xs...));
};
};
#define POLY(f) ([](auto... a){ return f(a...); })