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

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);