Beispiellösung
#ifndef MATRIX_HPP #define MATRIX_HPP #include <cassert> #include <cstddef> enum class StorageOrder {ColMajor, RowMajor}; template<typename T> struct Matrix { const std::size_t m; /* number of rows */ const std::size_t n; /* number of columns */ const std::size_t incRow; /* stride between subsequent rows */ const std::size_t incCol; /* stride between subsequent columns */ T* data; Matrix(std::size_t m, std::size_t n, StorageOrder order) : m(m), n(n), incRow(order == StorageOrder::ColMajor? 1: n), incCol(order == StorageOrder::RowMajor? 1: m), data(new T[m*n]) { } ~Matrix() { delete[] data; } Matrix(const Matrix&) = delete; Matrix& operator=(const Matrix&) = delete; const T& operator()(std::size_t i, std::size_t j) const { assert(i < m && j < n); return data[i*incRow + j*incCol]; } T& operator()(std::size_t i, std::size_t j) { assert(i < m && j < n); return data[i*incRow + j*incCol]; } }; #endif
#include <iomanip> #include <iostream> #include "matrix.hpp" int main() { Matrix<double> A(7, 8, StorageOrder::ColMajor); for (std::size_t j = 0; j < A.n; ++j) { for (std::size_t i = 0; i < A.m; ++i) { A(i, j) = i * A.n + j; } } std::cout << "A:" << std::endl; for (std::size_t i = 0; i < A.m; ++i) { for (std::size_t j = 0; j < A.n; ++j) { std::cout << " " << std::setw(8) << A(i, j); } std::cout << std::endl; } Matrix<float> B(3, 3, StorageOrder::RowMajor); for (std::size_t i = 0; i < B.m; ++i) { for (std::size_t j = 0; j < B.n; ++j) { B(i, j) = j * B.m + i; } } std::cout << std::endl << "B:" << std::endl; for (std::size_t i = 0; i < B.m; ++i) { for (std::size_t j = 0; j < B.n; ++j) { std::cout << " " << std::setw(8) << B(i, j); } std::cout << std::endl; } }
theon$ g++ -Wall -o test-matrix test-matrix.cpp theon$ ./test-matrix A: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 B: 0 3 6 1 4 7 2 5 8 theon$
Gibt es auch Datentypen, mit denen es nicht klappt?
An der folgenden Stelle setzt der new-Operator voraus, dass es einen parameterlosen Konstruktor für T gibt, da wir keine Parameter spezifizieren:
Matrix(std::size_t m, std::size_t n, StorageOrder order) : /* ... */ data(new T[m*n]) { }
Hier ist ein minimalistisches Beispiel mit der Klasse Double, die keinen parameterlosen Konstruktor zur Verfügung stellt:
#include <iomanip> #include <iostream> #include "matrix.hpp" struct Double { Double(double value) : value(value) { } double value; }; int main() { Matrix<Double> A(7, 8, StorageOrder::ColMajor); }
theon$ g++ -Wall -o bad-matrix bad-matrix.cpp In file included from bad-matrix.cpp:3: matrix.hpp: In instantiation of 'Matrix<T>::Matrix(std::size_t, std::size_t, StorageOrder) [with T = Double; std::size_t = long unsigned int]': bad-matrix.cpp:12:49: required from here matrix.hpp:21:15: error: no matching function for call to 'Double::Double()' 21 | data(new T[m*n]) { | ^~~~~~~~~~ bad-matrix.cpp:6:4: note: candidate: 'Double::Double(double)' 6 | Double(double value) : value(value) { | ^~~~~~ bad-matrix.cpp:6:4: note: candidate expects 1 argument, 0 provided bad-matrix.cpp:5:8: note: candidate: 'constexpr Double::Double(const Double&)' 5 | struct Double { | ^~~~~~ bad-matrix.cpp:5:8: note: candidate expects 1 argument, 0 provided bad-matrix.cpp:5:8: note: candidate: 'constexpr Double::Double(Double&&)' bad-matrix.cpp:5:8: note: candidate expects 1 argument, 0 provided theon$