boost::enable_if
-
Hallo,
Es geht leider immernoch um die Matrixklasse, die ich schon im Thread "Klassentemplate: nur einzelne Methoden spezialisieren" mit camper diskutiert habe (http://www.c-plusplus.net/forum/viewtopic-var-t-is-170900-and-postdays-is-0-and-postorder-is-asc-and-start-is-0.html). Wer mehr Infos will sollte da nachlesen, ich bin nur der Meinung, dass dashier ein neues Thema ist. Ich habe das, was camper geschrieben hat 1:1 kopiert (ok, Methodenname geändert, aber darf ja nichts ändern) und kann auch laut boost-Doku keine Fehler finden, aber es will einfach nicht. Egal wie viele verschiedene Varianten ich definiere, für jede nicht passende Variante kriege ich den Fehler hier:matrix.impl: In instantiation of »rayce::basic_matrix<double, 4, 5>«: main.cpp:11: instantiated from here matrix.impl:47: Fehler: kein Typ namens »type« in »struct boost::enable_if_c<false, void>«Das ist ja auch klar, aber ich rufe ja die Methode garnicht auf und soweit ich das mit SFINAE kapiert habe dürfte deswegen auch kein Fehler kommen: Die Liste der Überladungsmöglichkeiten ist halt leer. Doch selbst wenn ich dann noch eine zweite Methode mit enable_if<anderer Fall, void> deklariere kommen halt stattdessen zwei Fehler... Mein Compiler ist gcc 4.0.3, ein bisschen Codekontext:
template <typename T, int m, int n> class basic_matrix { //... typename boost::enable_if_c<m!=n,void>::type transpose(); typename boost::enable_if_c<m==n,void>::type transpose(); //... };Ob ich die Methoden implementiere spielt für den Fehler keine Rolle. Wie kriege ich diesen Fehler weg? Immerhin ist enable_if doch genau dafür gedacht!
geloescht
-
War ein kleiner Denkfehler von mir (den mach ich aber nicht zum ersten Mal). SFINAE wird nur bei Templates eingesetzt. transpose() selbst ist aber kein template. Um das ganze praktikabel in templateform zu bekommen, müssen wir ein wenig tricksen, wenn die funktion keine parameter hat:
template <typename T, int m, int n> class basic_matrix { //... template<bool> typename boost::enable_if_c<m!=n>::type do_transpose(); template<bool> typename boost::enable_if_c<m==n>::type do_transpose(); public: void transpose() { do_transpose<m==n>(); } //... };eigentlich bräuchen wir enable_if gar nicht ... wenn wir do_transpose für alle basic_matrix spezialisieren könnten...
-
Das ändert leider nichts bis darauf, dass der Fehler nun in der Linie gemeldet wird, wo ich do_transpose() deklariere:
matrix.hpp: In instantiation of »rayce::basic_matrix<double, 4, 4>«: main.cpp:10: instantiated from here matrix.hpp:64: Fehler: kein Typ namens »type« in »struct boost::enable_if_c<false, void>« matrix.impl: In instantiation of »rayce::basic_matrix<double, 4, 5>«: main.cpp:11: instantiated from here matrix.impl:48: Fehler: kein Typ namens »type« in »struct boost::enable_if_c<false, void>«Wieder etwas Kontext:
template <typename T, int m, int n> template <bool> typename boost::enable_if_c<m==n,void>::type basic_matrix<T, m, n>::do_transpose() { //... }Inzwischen habe ich enable_if auch mit normalen Funktionstemplates getestet und es funktioniert einwandfrei. Seltsame Sache, das...
-
Naja, wenn du eh eine do_transpose Methode hast, brauchst du doch kein enable_if oder Templates.
template <typename T, int m, int n> class basic_matrix { //... void do_transpose(boost::mpl::bool_<true> const&); void do_transpose(boost::mpl::bool_<false> const&); public: void transpose() { do_transpose(boost::mpl::bool_<m == n> ()); } //... };
-
Das habe ich nur gemacht, weil es laut camper das ganze zum laufen bringt - wenn es nach mir ginge wäre teilweises Spezialisieren von Methoden einfach möglich und Punkt. Mit diesem noch neueren Konstrukt bin ich noch mehr überfordert. Wobei natürlich... Ich schätze, falls ich da statt mpl::bool etwas nehme, was mehr als zwei Möglichkeiten erlaubt könnte ich mithilfe des ?:-Opertators mehr als 2 Fälle unterscheiden, richtig?
-
template <typename T, int m, int n> template <bool> typename boost::enable_if_c<m==n,void>::type basic_matrix<T, m, n>::do_transpose() { //... }IMHO ist das "template <bool>" hier falsch, da "typename boost::enable_if_c<m==n,void>::type" kein neues Template ist, sondern einfach ein Typ.
Lösch die Zeile einfach mal, dann müsste es eigentlich gehen.BTW: enable_if ist zwar "praktisch", aber leider auch "Der Blanke Horror (tm)".

-
Richtig.
Die Sache ist hier aber eigentlich ganz einfach. Es wird nur die normale Überladung verwendet. bool_<true> ist ein anderer Typ als bool_<false> und sie sind auch nicht ineinander konvertierbar. In der Transpose Methode wird nun einfach ein Objekt angelegt, dass den Typ bool_<true> oder bool_<false> hat und als Parameter von do_transpose übergeben. Das kann afaik auch vom Compiler wegoptimiert werden.
Mehrere Möglichkeiten bekommst du, wenn du zB int_ oder ähnliches verwendest, expressiver wirds, wenn du eigene Dummy-Typen einführst.
-
hustbaer:
Ich bin immer wieder erstaunt, wie viele Gestalten der blanke Horror (tm) annehmen kann...
.filmor: Nagut, morgen werde ich das versuchen. Wenn ich sage "der letzte Anlauf" dann ist das wohl Selbstbetrug, aber vielleicht schaffe ich es ja wirklich, das zum laufen zu bringen, damit ich keinen weiteren Anlauf brauche
-
ich sollte wohl erst mal testen, was ich vorschlage. SFINAE setzt voraus, das ein nicht instantiierbarer Parameter bzw. Rückgabewert abhängig (vom Templateparameter der Templatefunktion) ist. Das hier geht also z.B.
template <typename T, int m, int n> class basic_matrix { //... template<bool b> typename boost::enable_if_c<b==(m==n)>::type do_transpose(); template<bool b> typename boost::enable_if_c<b==(m!=n)>::type do_transpose(); public: void transpose() { do_transpose<m==n>(); } //... };Per überladung (nach .filmor) geht das nat. auch - wie Überhaupt fast alles durch Indirektion lösbar ist
. Einziger Nachteil der Überladungsvariante, die mir auf Anhieb einfällt, ist, dass die Templateklasse nicht mehr explizit instantiierbar ist, falls nicht alle Überladungen für die entsprechenden Parameter instantiierbar sind.
-
Kurze Frage: Was spricht eigentlich gegen freie Funktionen?
template<class T, unsigned M, unsigned N> matrix<T, N, M> transpose(const matrix<T, M, N>&); template<class T, unsigned N> T det(const matrix<T, N, N>&);
-
...: Mein OOP-Stolz.
So, ein herzliches Dankeschön an alle Helfer und Helfershelfer, denn es geht. Im Grunde habe ich einfach campers und .filmors Varianten zusammengeworfen und eine wenigstens einigermaßen elegante eigene Variante daraus zusammengebastelt://helper enum enum enabled_method { all, quadratic, quadratic_2, quadratic_3, quadratic_4 }; template <typename T, int m, int n> class basic_matrix { //... template <enabled_method e> typename boost::enable_if_c< e==quadratic || e==quadratic_2 || e==quadratic_3 || e == quadratic_4>::type do_transpose(); public: void transpose() { do_transpose< m==n? (m==2?quadratic_2: (m==3?quadratic_3: (m==4?quadratic_4: quadratic))): all >(); } //... };Wird zwar wieder etwas Redundanz für all diese Unterscheidungsmethoden, aber naja, jetzt bin ich den Umweg gegangen, da werde ich ihn nicht zurückgehen, nur weil ein anderer Weg vielleicht noch kürzer ist. Wenigstens gibt es schöne, aussagekräftige Fehlermeldungen:
matrix.hpp: In member function »void rayce::basic_matrix<T, m, n>::transpose() [with T = double, int m = 4, int n = 5]«: main.cpp:37: instantiated from here matrix.hpp:92: Fehler: keine passende Funktion für Aufruf von »rayce::basic_matrix<double, 4, 5>::do_transpose()«geloescht
Edit: Verbesserung und Fehlermeldung