Lösungsvorschlag
Hier ist ein Lösungsvorschlag:
#include <cstddef> /* needed for std::size_t */ #include <cstdio> /* needed for printf */ #include <cassert> /* needed for assert */ 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; const std::size_t incCol; 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; } 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]; } void init() { for (std::size_t i = 0; i < m; ++i) { for (std::size_t j = 0; j < n; ++j) { data[i*incRow + j*incCol] = j * n + i + 1; } } } void print() { for (std::size_t i = 0; i < m; ++i) { std::printf(" "); for (std::size_t j = 0; j < n; ++j) { /* be careful here, printf is not polymorph */ std::printf(" %4.1lf", (double) data[i*incRow + j*incCol]); } std::printf("\n"); } } }; int main() { Matrix<double> A(7, 8, StorageOrder::ColMajor); A.init(); std::printf("A =\n"); A.print(); Matrix<float> B(3, 3, StorageOrder::RowMajor); B.init(); std::printf("B =\n"); B.print(); }
Zu beachten ist hier die Methode print:
void print() { for (std::size_t i = 0; i < m; ++i) { std::printf(" "); for (std::size_t j = 0; j < n; ++j) { /* be careful here, printf is not polymorph */ std::printf(" %4.1lf", (double) data[i*incRow + j*incCol]); } std::printf("\n"); } }
Das printf-Format lf verlangt hier explizit einen long float-Parameter, also double. Hier erfolgt keine automatische Anpassung an den Template-Parameter T. Das Problem wird hier abgemildert, indem eine explizite Typkonvertierung zu double erfolgt in der Hoffnung, dass dies von T unterstützt wird und sinnvoll ist. Verallgemeinerbar ist dies nicht wirklich, da nicht notwendigerweise der gesamte Wertebereich von T in double repräsentierbar ist.
Und bei einigen Typen würde es überhaupt nicht funktionieren, d.h. sie könnten nicht für T eingesetzt werden. Beispiel:
struct Complex { double re; double im; }; int main() { Matrix<Complex> A(3, 3, StorageOrder::ColMajor); A.init(); std::printf("A =\n"); A.print(); }
Wenn wir dies zu übersetzen versuchen, setzt der Übersetzer in der Matrix-Klasse bei der Instantiierung überall T durch Complex und überprüft danach, ob das so erzeugte Resultat zulässig ist:
$shell> g++ -Wall -std=gnu++11 -o matrix_class9 matrix_class9.cpp matrix_class9.cpp: In instantiation of 'void Matrix::init() [with T = Complex]': matrix_class9.cpp:63:11: required from here matrix_class9.cpp:39:32: error: no match for 'operator=' (operand types are 'Complex' and 'std::size_t {aka unsigned int}') data[i*incRow + j*incCol] = j * n + i + 1; ^ matrix_class9.cpp:56:8: note: candidate: Complex& Complex::operator=(const Complex&) struct Complex { ^ matrix_class9.cpp:56:8: note: no known conversion for argument 1 from 'std::size_t {aka unsigned int}' to 'const Complex&' matrix_class9.cpp:56:8: note: candidate: Complex& Complex::operator=(Complex&&) matrix_class9.cpp:56:8: note: no known conversion for argument 1 from 'std::size_t {aka unsigned int}' to 'Complex&&' matrix_class9.cpp: In instantiation of 'void Matrix ::print() [with T = Complex]': matrix_class9.cpp:63:44: required from here matrix_class9.cpp:49:17: error: invalid cast from type 'Complex' to type 'double' std::printf(" %4.1lf", (double) data[i*incRow + j*incCol]); ^