非类型模板参数的自定义推导指南
User-defined deduction guides for non-type template parameters
我有一个矩阵的开头 class。这是代码-
template<int h, int w = h>
class mat {
public:
mat() : values(h, std::vector<double>(w)) {
if (w == h) {
int x = 0;
for (int y = 0; y < h; y++) {
values[y][x] = 1;
x++;
}
}
}
mat(std::initializer_list<std::vector<double>> matvals){
values = matvals;
}
mat(int val) : values(h, std::vector<double>(w, val)) {}
mat(const mat& m) {
values = m.values;
}
mat(mat&& m) {
values = std::move(m.values);
}
int width() {
return w;
}
int height() {
return h;
}
template<int mh, int mw = mh>
auto operator*(const mat<mh, mw>& m) -> mat<h, mw> const {
if (w != mh) throw std::logic_error{ "Matrices cannot be multiplied" };
mat<h, mw> temp;
std::vector<double> mcol(mh);
for (int y = 0; y < mw; y++) {
for (int mx = 0; mx < mw; mx++) {
for (int my = 0; my < mh; my++) {
mcol[my] = m.values[my][mx];
}
temp.values[y % h][mx] = dot(values[y % h], mcol);
}
}
return temp;
}
mat operator+(const mat& m) const {
mat temp;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
temp.values[y][x] = values[y][x] + m.values[y][x];
}
}
return temp;
}
std::vector<double>& operator[](int y) {
return values[y];
}
mat& operator=(const mat& m) {
values = m.values;
return *this;
}
mat& operator=(mat&& m) {
values = std::move(m.values);
return *this;
}
private:
std::vector<std::vector<double>> values;
template<int mw, int mh>
friend class mat;
};
到目前为止,class 是这样使用的-
mat<2, 4> mat1 = {
{1, 2, 3, 4},
{5, 6, 7, 8},
};
mat<4, 3> mat2 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12}
};
auto mat3 = mat1 * mat2;
注意到冗余了吗?如果用户想要使用 std::initializer_list
构造函数创建矩阵,则他们必须首先在模板参数中指定宽度和高度。此外,还有一个问题是,如果他们使用 std::initializer_list
其尺寸与模板参数中指定的尺寸不同,则行为将是未定义的。如何编写非类型模板参数的推导指南?我知道如何使用基本模板来完成它,但我尝试像您那样做的每件事通常都会出现许多编译器错误。这是期望的行为-
mat mat1 = {
{1, 2, 3, 4},
{5, 6, 7, 8},
};
mat mat2 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12}
};
auto mat3 = mat1 * mat2;
编辑:
任何真正想要制作矩阵 class 的人都不应该制作宽度和高度模板参数。它只是不必要地使事情复杂化。
你可以获得
mat mat2 = {
row{1, 2, 3},
row{4, 5, 6},
row{7, 8, 9},
row{10, 11, 12}
};
上班;写一个一维行,从 Ts&&...
推导出它的大小并类型检查 Ts
匹配,然后让 mat
成为一行一维行(或重新实现相同的机器)。
我不知道是否有一种方法可以避免必须显式键入(好吧,有点)“行”,同时还获得一个固定长度来推断矩阵类型。
一个有趣的游戏是用行乘法来定义矩阵乘法,一个矩阵mult b是一个mult2 b转置,其中a mult2 b是矩阵a_i内积b_j。
当然那个兔子洞一直在继续。
如果您提供这样的构造函数:
template<int w, int h>
struct A{
A(double (&&c)[w][h]); // `double const (&c)[w][h]` is also OK.
};
没有其他推导指南,您可以按如下方式使用:
A a{{
{1, 2, 3},
{4, 5, 6}
}};
A b = {{
{1, 2, 3},
{4, 5, 6}
}};
A c({
{1, 2, 3},
{4, 5, 6}
});
但我们可能觉得外括号很糟糕,所以我们必须提供一个特殊的构造函数和推导指南:
namespace Impl{
template<typename T, typename = void>
struct helper;
template<int h1, int... hs>
struct helper<std::integer_sequence<int, h1, hs...>, std::enable_if_t<((h1 == hs) && ...)>>{
static constexpr int w = sizeof...(hs) + 1;
static constexpr int h = h1;
};
template<int... hs>
inline constexpr int helper_w = helper<std::integer_sequence<int, hs...>>::w;
template<int... hs>
inline constexpr int helper_h = helper<std::integer_sequence<int, hs...>>::h;
}
template<int w, int h>
struct A{
template<int... hs, typename = std::enable_if_t<w + 1 == Impl::helper_w<h, hs...>>>
A(double (&&... head)[hs]);
};
template<int... hs>
A(double (&&... head)[hs]) -> A<Impl::helper_w<hs...>, Impl::helper_h<hs...>>;
然后就可以随意使用了:
A a{
{1, 2, 3},
{4, 5, 6}
};
A b = {
{1, 2, 3},
{4, 5, 6}
};
我有一个矩阵的开头 class。这是代码-
template<int h, int w = h>
class mat {
public:
mat() : values(h, std::vector<double>(w)) {
if (w == h) {
int x = 0;
for (int y = 0; y < h; y++) {
values[y][x] = 1;
x++;
}
}
}
mat(std::initializer_list<std::vector<double>> matvals){
values = matvals;
}
mat(int val) : values(h, std::vector<double>(w, val)) {}
mat(const mat& m) {
values = m.values;
}
mat(mat&& m) {
values = std::move(m.values);
}
int width() {
return w;
}
int height() {
return h;
}
template<int mh, int mw = mh>
auto operator*(const mat<mh, mw>& m) -> mat<h, mw> const {
if (w != mh) throw std::logic_error{ "Matrices cannot be multiplied" };
mat<h, mw> temp;
std::vector<double> mcol(mh);
for (int y = 0; y < mw; y++) {
for (int mx = 0; mx < mw; mx++) {
for (int my = 0; my < mh; my++) {
mcol[my] = m.values[my][mx];
}
temp.values[y % h][mx] = dot(values[y % h], mcol);
}
}
return temp;
}
mat operator+(const mat& m) const {
mat temp;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
temp.values[y][x] = values[y][x] + m.values[y][x];
}
}
return temp;
}
std::vector<double>& operator[](int y) {
return values[y];
}
mat& operator=(const mat& m) {
values = m.values;
return *this;
}
mat& operator=(mat&& m) {
values = std::move(m.values);
return *this;
}
private:
std::vector<std::vector<double>> values;
template<int mw, int mh>
friend class mat;
};
到目前为止,class 是这样使用的-
mat<2, 4> mat1 = {
{1, 2, 3, 4},
{5, 6, 7, 8},
};
mat<4, 3> mat2 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12}
};
auto mat3 = mat1 * mat2;
注意到冗余了吗?如果用户想要使用 std::initializer_list
构造函数创建矩阵,则他们必须首先在模板参数中指定宽度和高度。此外,还有一个问题是,如果他们使用 std::initializer_list
其尺寸与模板参数中指定的尺寸不同,则行为将是未定义的。如何编写非类型模板参数的推导指南?我知道如何使用基本模板来完成它,但我尝试像您那样做的每件事通常都会出现许多编译器错误。这是期望的行为-
mat mat1 = {
{1, 2, 3, 4},
{5, 6, 7, 8},
};
mat mat2 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12}
};
auto mat3 = mat1 * mat2;
编辑: 任何真正想要制作矩阵 class 的人都不应该制作宽度和高度模板参数。它只是不必要地使事情复杂化。
你可以获得
mat mat2 = {
row{1, 2, 3},
row{4, 5, 6},
row{7, 8, 9},
row{10, 11, 12}
};
上班;写一个一维行,从 Ts&&...
推导出它的大小并类型检查 Ts
匹配,然后让 mat
成为一行一维行(或重新实现相同的机器)。
我不知道是否有一种方法可以避免必须显式键入(好吧,有点)“行”,同时还获得一个固定长度来推断矩阵类型。
一个有趣的游戏是用行乘法来定义矩阵乘法,一个矩阵mult b是一个mult2 b转置,其中a mult2 b是矩阵a_i内积b_j。
当然那个兔子洞一直在继续。
如果您提供这样的构造函数:
template<int w, int h>
struct A{
A(double (&&c)[w][h]); // `double const (&c)[w][h]` is also OK.
};
没有其他推导指南,您可以按如下方式使用:
A a{{
{1, 2, 3},
{4, 5, 6}
}};
A b = {{
{1, 2, 3},
{4, 5, 6}
}};
A c({
{1, 2, 3},
{4, 5, 6}
});
但我们可能觉得外括号很糟糕,所以我们必须提供一个特殊的构造函数和推导指南:
namespace Impl{
template<typename T, typename = void>
struct helper;
template<int h1, int... hs>
struct helper<std::integer_sequence<int, h1, hs...>, std::enable_if_t<((h1 == hs) && ...)>>{
static constexpr int w = sizeof...(hs) + 1;
static constexpr int h = h1;
};
template<int... hs>
inline constexpr int helper_w = helper<std::integer_sequence<int, hs...>>::w;
template<int... hs>
inline constexpr int helper_h = helper<std::integer_sequence<int, hs...>>::h;
}
template<int w, int h>
struct A{
template<int... hs, typename = std::enable_if_t<w + 1 == Impl::helper_w<h, hs...>>>
A(double (&&... head)[hs]);
};
template<int... hs>
A(double (&&... head)[hs]) -> A<Impl::helper_w<hs...>, Impl::helper_h<hs...>>;
然后就可以随意使用了:
A a{
{1, 2, 3},
{4, 5, 6}
};
A b = {
{1, 2, 3},
{4, 5, 6}
};