【问题标题】:Automatic size deduction for two-dimensional array二维数组的自动减尺寸
【发布时间】:2020-08-02 00:07:48
【问题描述】:

我正在尝试制作一个固定大小的矩阵类。目的是让它继承或利用std::array 的std::array:

template <size_t Rows, size_t Columns, class T=double>
struct Matrix : public std::array<std::array<T, Columns>, Rows> {
};

我想提供一个可以自动推断大小的初始化程序,就像 C++17(我正在使用的)中的 std::array 一样。我也可以使用函数来制作Matrix,而不是使用类模板参数推导。

// What I want, but does not work:
Matrix matrix {{1., 2.},
               {3., 4.}};

// Or:
auto matrix = MakeMatrix ({1., 2.},
                          {3., 4.});

我没能让其中任何一个成为可能。相反,只有以下工作:

// Requires specifying the size and repeating `std::array`, both undesired
Matrix<2,2> mat {
    std::array{1., 2.},
    std::array{3., 4.}
};

// OR this, which requires specifying the size and is generally confusing
Matrix<2,2> mat2 {1., 2., 
                  3., 4.};

我尝试使用可变参数模板,但这也没有吸引编译器:

template<class... Args>
auto MakeMatrix (Args... args) {
  return Matrix{ std::array {args} ... };
}

// This causes compiler error:
// error: no matching function for call to 'MakeMatrix'
// candidate function [with Args = <>] not viable: requires 0 arguments, but 2 were provided
auto matrix = MakeMatrix ({1., 2.},
                          {3., 4.});

// This causes compiler error
// error: no viable constructor or deduction guide for deduction of template arguments of 'Matrix'
// note: in instantiation of function template specialization 'MakeMatrix<std::__1::array<double, 2>, std::__1::array<double, 2> >'
// note: note: candidate function template not viable: requires 0 arguments, but 2 were provided
auto matrix = MakeMatrix (std::array {1., 2.},
                          std::array {3., 4.});

我也考虑过使用std::initializer_list&lt;std::initializer_list&lt;T&gt;&gt;,但是据我所知这些不支持固定大小,我希望在编译时确定大小。

关于如何做到这一点的任何想法,还是用当前的 C++ 机制是不可能的?

【问题讨论】:

  • 避免从 std 类型继承——疯狂去那里。使用组合(封装一个 std::array 就可以了)。
  • 请注意,标准容器并非设计为具有多态行为。标准容器的公共继承使您面临潜在风险。 Link。您可以私下继承,拥有一个std::array 成员,或者如果Matrix 非常薄,请改用using 类型别名。
  • 查看this question 获取make_array 获取一维std::array;我希望它可以推广到二维。一般来说,我建议不要使用这种东西;可变参数模板往往会变得混乱。

标签: c++ templates matrix c++17 stdarray


【解决方案1】:

问题是编译器在用作参数时无法推断出{}。这适用于initializer_list(对于构造函数,由于一些特殊规则)。但是你错过了尺寸。

一种解决方法是内置数组:

template <typename T, size_t N>
using Row = const T (&)[N]; // for readability

template <auto Rows, auto Columns, typename T = double>
class Matrix {
public:
  template <typename... Ts, auto N>
  Matrix(Row<Ts, N>... rows) {}
};

template <typename... RowTypes, auto Columns>
Matrix(Row<RowTypes, Columns>...)
    -> Matrix<sizeof...(RowTypes), Columns, std::common_type_t<RowTypes...>>;

您现在可以完全按照您的喜好构造Matrix:

const auto m = Matrix{{1, 2}, {1, 2}, {1, 2}};

对于最后一步,使用内置数组初始化 std::array 可能会很棘手。 C++20 提供了一个function,检查链接以获取可能的实现。如果您复制该实现,或者有一个可用的实现,您可以轻松地创建构造函数,如下所示:

template <auto Rows, auto Columns, typename T = double>
class Matrix {
public:
  template <typename... Ts, auto N>
  Matrix(Row<Ts, N>... rows) {
    data_ = {to_array(rows)...};
  }
private:
  std::array<std::array<T, Columns>, Rows> data_;
};

Live example, with operator[] to show that the data layout is correct.

【讨论】:

  • 这是一个了不起的解决方案!我很好奇你是否特别从任何地方学到了这一点,以便我可以阅读更多内容。
  • This proposal 解释了如何从数组中推断出大小和类型。我认为其他一切都是正常模板编程。有关更多详细信息,请查看this book,它几乎涵盖了有关模板的所有内容(包括引用数组推导的几个部分)。
猜你喜欢
  • 1970-01-01
  • 2020-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多