I have this Matrix class that allocates the data on the heap and a helper class M that is a Matrix with the data as C-style array, no allocation. Template argument deduction works for the helper class automatically. But not for the Matrix class.
I can construct a Matrix from that with template argument deduction:
M m{{{1, 2}, {3, 4}}};
Matrix a{M{{{1, 2}, {3, 4}}}};
What I'm looking for is to get rid of the helper class so the following works:
Matrix a{{{1, 2}, {3, 4}}};
Here is a working example with the helper class: https://godbolt.org/z/46vEqbvax
#include <cstddef>
#include <type_traits>
#include <cstring>
#include <initializer_list>
#include <utility>
#include <memory>
#include <cassert>
#include <algorithm>
template <typename T, std::size_t rows, std::size_t cols>
class M {
public:
const T * operator[](std::size_t x) const {
return data[x];
}
T * operator[](std::size_t x) {
return data[x];
}
T data[rows][cols]{};
};
template <typename T, std::size_t rows, std::size_t cols>
class Matrix {
private:
T *data{new T[rows * cols]};
public:
Matrix() { }
~Matrix() {
delete[] data;
data = nullptr; // crash on use after free
}
Matrix(const Matrix &other) {
*this = other;
}
Matrix(T &&other) : data(other.data) {
other.data = nullptr;
}
Matrix & operator=(const Matrix &other) {
if constexpr (std::is_aggregate_v<T>) {
memcpy(data, other.data, sizeof(T) * rows * cols);
} else {
for (std::size_t i = 0; i < rows; i) {
for (std::size_t j = 0; j < cols; j) {
(*this)[i][j] = other[i][j];
}
}
}
return *this;
}
Matrix operator=(Matrix &&other) {
swap(data, other.data);
return *this;
}
const T * operator[](std::size_t x) const {
return &data[x * cols];
}
T * operator[](std::size_t x) {
return &data[x * cols];
}
Matrix(const M<T, rows, cols>& other) {
if constexpr (std::is_aggregate_v<T>) {
memcpy(data, other.data, sizeof(T) * rows * cols);
} else {
for (std::size_t i = 0; i < rows; i) {
for (std::size_t j = 0; j < cols; j) {
(*this)[i][j] = other[i][j];
}
}
}
}
Matrix(M<T, rows, cols>&& other) {
if constexpr (std::is_aggregate_v<T>) {
memcpy(data, other.data, sizeof(T) * rows * cols);
} else {
for (std::size_t i = 0; i < rows; i) {
for (std::size_t j = 0; j < cols; j) {
std::swap((*this)[i][j], other[i][j]);
}
}
}
}
};
//template <typename T, std::size_t rows, std::size_t cols>
//Matrix(M<T, rows, cols>) -> Matrix<T, rows, cols>;
#include <iostream>
int main() {
Matrix a{M{{{1, 2}, {3, 4}}}};
Matrix b{a};
std::cout << b[0][0] << " " << b[0][1] << std::endl;
std::cout << b[1][0] << " " << b[1][1] << std::endl;
}
CodePudding user response:
You can get rid of helper class M
and have your Matrix
template parameters automatically deduced by changing the type of your constructor parameters to (l- and r-value references to) C-style arrays:
//Matrix(const M<T, rows, cols>& other) {
Matrix(const T (&other)[rows][cols]) {
// ... same implementation ...
}
//Matrix(M<T, rows, cols>&& other) {
Matrix(T (&&other)[rows][cols]) {
// ... same implementation ...
}