非类型模板参数的用户定义扣除指南

3

我有一个矩阵类的雏形。以下是代码 -

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;
};

目前这个类的用法如下:

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;

编辑: 任何想要创建矩阵类的人都不应该将宽度和高度作为模板参数。这只会使事情变得更加复杂。
编辑2: 无论如何,这个例子都是荒谬的。我将宽度和高度作为模板参数,但使用std::vector来存储值。

2个回答

2
如果您提供了如下构造函数:
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}
};

这个可行!非常感谢!我不知道为什么在msvc下编译有问题,但是在minGW下编译很顺利。我想我可能会转向使用MinGW。这种烂事总是发生。 - Jcsq6
我以前从未使用过c++17,所以花了一点时间才弄清楚“折叠表达式”是什么。 - Jcsq6
@Jcsq6 在使用 MSVC 时,使用折叠表达式似乎会出现编译器错误。如果你需要在 MSVC 上编译它,使用 std::conjunction_v 而不是折叠表达式将有所帮助。 - RedFog
@RedFrog 哇,谢谢你的提示!那会帮很多忙! - Jcsq6

1

你可以获得

mat mat2 = {
  row{1, 2, 3},
  row{4, 5, 6},
  row{7, 8, 9},
  row{10, 11, 12}
};

需要做的是编写一个1-D行,从Ts&&...中推断其大小并进行类型检查,然后使mat成为1-D行的行(或重新实现相同的机制)。

我不知道有没有一种避免明确输入“rows”并同时获得consteval长度以推断矩阵类型的方法。

一个有趣的游戏是通过行乘法来定义矩阵乘法,矩阵a乘以b是a mult2 b转置,其中mult2 b是矩阵a_i和b_j的内积。

当然,这个兔子洞还在继续。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接