Автоматическое вычитание c размера для двумерного массива - PullRequest
4 голосов
/ 02 августа 2020

Я пытаюсь создать матричный класс фиксированного размера. Намерение состоит в том, чтобы заставить его наследовать или использовать std::array из std::array:

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

Я хотел бы предоставить инициализатор, который может автоматически определять размер, например std::array может в C ++ 17 (который я использую). Я также хорошо отношусь к использованию функции для создания 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.};

Я пробовал использовать вариативные c шаблоны, но это тоже не понравилось компилятору:

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<std::initializer_list<T>>, однако они не поддерживают фиксированный размер, насколько я могу судить, и я хочу, чтобы размер определялся во время компиляции.

Любые мысли о том, как это сделать, или это просто невозможно с текущим C ++ механика?

1 Ответ

6 голосов
/ 02 августа 2020

Проблема в том, что компилятор не может вывести {} при использовании в качестве аргумента. Это работает с 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 предоставляет функцию , проверьте ссылку на возможные реализации. Если вы скопируете эту реализацию или у вас есть одна доступная, вы можете легко создать конструктор, как:

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

Живой пример с operator[], чтобы показать, что макет данных правильный .

...