==================================================== Verschiedene Ausprägungen einer polymorphen Funktion ==================================================== Bislang sahen alle Ausprägungen einer Template-Klasse oder einer Template-Funktion gleich aus. Sie unterschieden sich nur durch das Einsetzen unterschiedlicher Typparameter. Es ist aber auch möglich, unterschiedliche Varianten für einzelne Datentypen festzulegen. Dies kommt uns zu Hilfe bei der Ausgabe einer Matrix. Die bisherige Fassung von `print_matrix` hoffte darauf, dass die jeweiligen Werte in `double` konvertierbar sind, was nicht ohne weiteres der Fall ist. Die Idee ist, dass die Ausgabe eines einzelnen Werts von dem der gesamten Matrix getrennt wird: ---- CODE (type=cpp) ---------------------------------------------------------- template void print_matrix(const Matrix& A) { for (std::size_t i = 0; i < A.m; ++i) { std::printf(" "); for (std::size_t j = 0; j < A.n; ++j) { print_value(A(i, j)); } std::printf("\n"); } } ------------------------------------------------------------------------------- Die neue Funktion `print_value` kann dann für verschiedene Varianten überladen werden: ---- CODE (type=cpp) ---------------------------------------------------------- void print_value(long double value) { std::printf(" %4.1Lf", value); } void print_value(double value) { std::printf(" %4.1lf", value); } void print_value(float value) { std::printf(" %4.1f", value); } ------------------------------------------------------------------------------- Wenn dann jedoch eine Matrix von ganzen Zahlen angelegt wird, kommt es zu einem Fehler, weil für den Übersetzer alle angebotenen Varianten gleichrangig erscheinen (ganze Zahlen lassen sich in `float`, `double` oder `long double` konvertieren) und daher kein deterministisches Auswahlkriterium existiert: ---- CODE (type=cpp) ---------------------------------------------------------- int main() { Matrix A(1, 1, StorageOrder::ColMajor); std::printf("A =\n"); print_matrix(A); } ------------------------------------------------------------------------------- ---- SHELL (path=session9) -------------------------- g++ -Wall -std=gnu++11 -o matrix_class15 matrix_class15.cpp ----------------------------------------------------- In solchen Fällen kann es eine Lösung sein, eine Template-Funktion des gleichen Namens hinzuzufügen. Diese wird aufgerufen, wenn keine der anderen Funktionen direkt anwendbar ist: ---- CODE (type=cpp) ---------------------------------------------------------- template void print_value(const T& value) { std::printf(" %4.1Lf", (long double) value); } ------------------------------------------------------------------------------- Hier wird der Versuch unternommen, den Wert in `long double` zu konvertieren. Aber auch diese Variante wird nicht funktionieren, wenn Datentypen wie beispielsweise `Complex` verwendet werden. Aufgabe ======= Die GMP-Bibliothek (_GMP_ steht für GNU _multiple precision library_) stellt mehrere numerische Datentypen für höhere Genauigkeiten bereit. Unter anderem gibt es in dieser Bibliothek den Datentyp `mpq_class` für rationale Zahlen, d.h. der Nenner und der Zähler werden als getrennte, ganzzahlige Werte geführt. Hier ist ein Beispiel: ---- CODE (type=cpp) ---------------------------------------------------------- #include #include int main() { mpq_class a(1, 3); /* one third */ mpq_class b(2, 5); /* two fifth */ mpq_class c = a + b; std::printf("%9s\n", c.get_str().c_str()); } ------------------------------------------------------------------------------- ---- SHELL (path=session9) -------------------------- g++ -Wall -std=gnu++11 -o test_mpq test_mpq.cpp -lgmp test_mpq ----------------------------------------------------- Passen Sie die Vorlage so an, dass die `print_value`-Funktion auch rationale Zahlen unterstützt. Fügen Sie eine Template-Funktion `scale_matrix` hinzu, die eine Matrix mit einem Wert multipliziert. Nutzen Sie diese, um das alles mit rationalen Zahlen zu testen. Vorlage ======= :import: session9/matrix_class15.cpp :navigate: up -> doc:index back -> doc:session9/page06