Template-Funktionen
Content |
Momentan musste der Programmtext für die Ausgabe der beiden Matrizen dupliziert werden:
Matrix<double> A(7, 8, StorageOrder::ColMajor); /* ... init A ... */ 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); /* ... init B ... */ 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; }
Das ist natürlich nicht sehr schön. Eine denkbare Option wäre es, die Ausgabe als Methode in die Matrix-Klasse zu integrieren. Aber das ist im Sinne des Software Engineering nicht wünschenswert. Stattdessen sollten wir danach streben, Klassen zu entwickeln, die
-
minimalistisch und
-
im ausreichenden Maße erweiterbar sind, so dass zukünftige Anwendungen nicht auf unüberwindbare Hindernisse stoßen.
Da die Matrix-Klasse bereits die Zugriff auf individuelle Elemente unterstützt, könnten wir dies durch eine separate Funktion erledigen. Für die ursprüngliche Matrix-Klasse nur für double könnte dies wie folgt umgesetzt werden:
void print_matrix(const Matrix& A) { 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; } }
Da C++ Template-Funktionen unterstützt, lässt sich das auch mit einer Template-Funktion erledigen, die identisch zu oben ist, aber um ein Template-Konstrukt ergänzt wird:
#include <iomanip> #include <iostream> #include "matrix.hpp" template<typename Matrix> void print_matrix(const Matrix& A) { 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; } } 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; print_matrix(A); 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; print_matrix(B); }
theon$ g++ -Wall -o print-matrix print-matrix.cpp theon$ ./print-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$
Wie Sie hier sehen, werden beim Aufruf der Template-Funktion die Template-Parameter nicht angegeben. Diese können in der Tat entfallen, wenn der Übersetzer die Möglichkeit hat, diese selbst anhand der übergebenen Parameter herzuleiten.
Aufgabe
Ergänzen Sie das Beispiel um eine init-Funktion. Sie können es zuerst mit einer Variante tun, die die Initialisierung wie jetzt zuvor in einer festgelegten Weise durchführt. Reizvoll wäre hier aber der Einsatz von Lambda-Ausdrücken, für die Sie dann einen zweiten Template-Parameter benötigen würden. Anwendungsbeispiel:
Matrix<double> A(7, 8, StorageOrder::ColMajor); init_matrix(A, [&A](std::size_t i, std::size_t j) { return i * A.n + j; }); std::cout << "A:" << std::endl; print_matrix(A); Matrix<float> B(3, 3, StorageOrder::RowMajor); init_matrix(B, [&B](std::size_t i, std::size_t j) { return j * B.m + i; }); std::cout << std::endl << "B:" << std::endl; print_matrix(B);