================== Generische Klassen ================== Bislang unterstützte die Matrix-Klasse nur den Datentyp `double`: ---- CODE (type=cpp) ---------------------------------------------------------- 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; double* data; /* ... */ const double& operator()(std::size_t i, std::size_t j) const { assert(i < m && j < n); return data[i*incRow + j*incCol]; } double& operator()(std::size_t i, std::size_t j) { assert(i < m && j < n); return data[i*incRow + j*incCol]; } /* ... */ }; ------------------------------------------------------------------------------- Wenn wir analog dazu Interesse an einer Matrix-Klasse hätten für den Datentyp `float`, könnte die bisherige Klasse übernommen und überall `double` durch `float` ersetzt werden. Sie würde funktionieren, dürfte aber nicht mehr `struct Matrix` genannt werden bzw. müsste einen eigenen Namensraum erhalten, um nicht in Konflikt zur anderen Klasse zu stehen. Die textuelle Vervielfältigung einer solchen Matrix-Klasse erscheint jedoch sehr unvorteilhaft, weil dann statt einer Fassung viele weitgehend textuell identische Varianten zu pflegen sind. In der Informatik gibt hier schon seit längerer Zeit den Ansatz zur generischen Programmierung. Hierbei wird ein Teil des Programmtexts wie beispielsweise eine Klasse mit einem Typen parametrisiert und entsprechend generisch formuliert. Ein generisches Konstrukt kann dann für einen konkreten Typ instantiiert werden und ist dann in dieser Form nutzbar. In C++ werden die generischen Programmkonstrukte Templates genannt. Ein Template kann eine Deklaration (wie beispielsweise eine Klasse oder eine Funktion) mit einem oder mehreren Typen parametrisieren. (Es sind auch andere Template-Parameter möglich.) Im Beispiel der Matrix-Klasse würde uns zunächst ein Parameter für den Elementtyp genügen, den wir hier `T` nennen: ---- CODE (type=cpp) ---------------------------------------------------------- template 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; /* ... */ 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]; } /* ... */ }; ------------------------------------------------------------------------------- Eine Template-Deklaration * beginnt mit dem Schlüsselwort `template`, * nennt dann alle Parameter in gewinkelten Parametern, wobei Typparameter mit `typename` gekennzeichnet werden und * wird dann mit der eigentlichen Deklaration abgeschlossen, in der die Template-Parameter ganz normal verwendet werden dürfen. Nach der obigen Template-Deklaration ist `Matrix` keine reguläre Klasse mehr, sondern eine Template-Klasse. Nutzbar wird sie erst durch eine Instantiierung, d.h. die Template-Parameter müssen konkretisiert werden. Dies ist möglich, indem innerhalb der Winkel die konkreten Parameter angegeben werden. Im folgenden Beispiel werden die Matrizen `A` und `B` deklariert. `A` verwendet `double` als Elementtyp, während `B` auf `float` basiert. ---- CODE (type=cpp) ---------------------------------------------------------- Matrix A(7, 8, StorageOrder::ColMajor); Matrix B(3, 3, StorageOrder::RowMajor); ------------------------------------------------------------------------------- Aufgabe ======= Verwandeln Sie die `struct Matrix` der Vorlage in eine Template-Klasse wie oben beschrieben und testen Sie sie für verschiedene Datentypen. Welche Methode von `struct Matrix` lässt sich nicht trivial anpassen? Gibt es Typen, die bei `Matrix` nicht als Parameter eingesetzt werden dürfen? Vorlage ======= :import: session7/matrix_class7.cpp :navigate: up -> doc:index next -> doc:session9/page02