[Erledigt] boost::enable_if, Vermeiden von Codeduplikation
-
Hallo,
ich stehe gerade auf dem Schlauch. Ich möchte eine Iterator-Klasse implementieren, allerdings brauche ich die einmal in der normalen und einmal als 'const_iterator'-Variante. Um Codeduplikation zu vermeiden, habe ich die Klasse einfach als Template geschrieben und dann zwei Typedefs definiert (der Iterator selbst ist nur ein recht einfacher Wrapper um einen anderen Iterator).
Allerdings braucht die const-Version einen zusätzlichen Konstruktor, der die Initialisierung durch Konvertierung von einem non-const-Objekt erlaubt. Spezialisierung klappt nicht (oder kann ich dann den anderen Code „erben“?). Also dachte ich an 'boost::enable_if, obwohl der Konstruktor dann ein zweites Dummy-Argument hätte, was ich nicht besonders schick finde.
… leider klappt das nicht so, wie ich will. Der Compiler (MSVC9) beschwert sich, „'type': is not a member of 'boost::enable_if<Cond>'“; bei folgendem Code:
template <typename ListIter> class list_iterator : public std::iterator< /* … */> { public: list_iterator() : m_nodeptr() { } list_iterator(ListIter nodeptr) : m_nodeptr(nodeptr) { } list_iterator( list_iterator<node_iterator> const& other, typename boost::enable_if< typename boost::template is_same<ListIter, const_node_iterator>::type >::type* dummy = 0 ) : m_nodeptr(other.nodeptr()) { } };('m_nodeptr' bzw. 'nodeptr()' sind entsprechend definierte Mitglieder.)
'node_iterator' bzw 'const_node_iterator' sind dabei wie folgt definiert:
typedef std::list< node, typename allocator_type::template rebind<node>::other > list_type; typedef typename list_type::iterator node_iterator; typedef typename list_type::const_iterator const_node_iterator;Ich habe 'boost::enable_if' bisher noch nie benutzt und SFINAE selbst auch nur sehr wenig. Ich denke daher, dass das ein grundsätzlicher Fehler ist, oder?
(Langsam beginne ich zu denken, dass es wesentlich wartbarer wäre, die zwei Iterator-Typen getrennt doppelt zu implementieren.)
-
explizite spezialisierung ist keine lösung?
template <> class list_iterator<node_iterator> :: list_iterator ( list_iterator<node_iterator> const& other ) { //= copy ctor } template <> class list_iterator<const_node_iterator>::list_iterator ( list_iterator<node_iterator> const& other ) { //= what you want } //copy ctor für list_iterator<const_node_iterator> wird automatisch erstellt
-
Der Konstruktor list_iterator ist zwar Member eines Templates, aber selbst eine normale Funktion, SFINAE bezieht sich aber nur auf Funktionstemplates. boost::is_same ist übrigens kein abhänger Name, das Schlüsselwort template hat dort nichts verloren, und is_same ist bereits eine Metafunktion, wie enable_if sie benötigt, das ::type kann man sich also sparen.
template<typename T> list_iterator( list_iterator<T> const& other, typename boost::enable_if_c< boost::is_same<T, node_iterator>::value && boost::is_same<ListIter, const_node_iterator>::value >::type* dummy = 0 ) : m_nodeptr(other.nodeptr()) { }
-
camper schrieb:
boost::is_same ist übrigens kein abhänger Name, das Schlüsselwort template hat dort nichts verloren,
richtig, aber ich glaube, nicht aus diesem grund.
14.2/5 würde template hier dennoch erlauben, der grund, warum es trotzdem ill-formed ist, ist bloß, weil is_same kein member-template ist.
-
queer_boy schrieb:
camper schrieb:
boost::is_same ist übrigens kein abhänger Name, das Schlüsselwort template hat dort nichts verloren,
richtig, aber ich glaube, nicht aus diesem grund.
14.2/5 würde template hier dennoch erlauben, der grund, warum es trotzdem ill-formed ist, ist bloß, weil is_same kein member-template ist.Ja. Allerdings sind typename und template nur bei abhängigen Namen (was Member impliziert) notwendig. Die Vereinfachung der Regel erleichtert das Schreiben in Fällen, wo es nicht auf Anhieb offensichtlich ist, ob ein Name abhängig ist. Von einem inflationären Gebrauch davon - insbesondere mit C++0x - halte ich aber nichts. Eine Stelle, an der diese Schlüsselworte nicht benötigt werden, wird durch sie im Allgemeinen nicht besser werden.
-
camper schrieb:
Der Konstruktor list_iterator ist zwar Member eines Templates, aber selbst eine normale Funktion, SFINAE bezieht sich aber nur auf Funktionstemplates.
Autsch. Logisch.
boost::is_same ist übrigens kein abhänger Name, das Schlüsselwort template hat dort nichts verloren
Noch'n Autsch.

und is_same ist bereits eine Metafunktion, wie enable_if sie benötigt, das ::type kann man sich also sparen.
Jupp, aber da es ohne nicht funktionierte, habe ich es explizit hingeschrieben, um alle möglichen Fehlerquellen auszuschließen.
-
Übrigens:
- Explizite Template-Spezialisierung des Kopierkonstruktors funktioniert nicht. Ich weiß nicht genau wo der Wurm drin ist. Der Compiler meckert, ich würde ein Member definieren, das ich nicht deklariert habe (obwohl ich es sehr wohl deklariert habe).
- Aus dem Kopierkonstruktor ein Template zu machen ist auch keine Lösung. Die Initialisierung wird ja für den folgenden Fall gebraucht, und da wird AFAIK kein Template korrekt hin aufgelöst, oder?
mycontainer_type mycont; // ruft die non-const-Version auf. mycontainer_type::const_iterator = mycont.begin();D.h. hier wird implizit konstruiert.
… ist aber alles egal, ich habe es jetzt nochmal mit einer Konvertierungsfunktion versucht und das klappt super. Ohne SFINAE, ohne partielle Spezialisierung:
operator list_iterator<const_node_iterator> () const { return list_iterator<const_node_iterator>(m_nodeptr); }
-
Konrad Rudolph schrieb:
Übrigens:
- Explizite Template-Spezialisierung des Kopierkonstruktors funktioniert nicht. Ich weiß nicht genau wo der Wurm drin ist. Der Compiler meckert, ich würde ein Member definieren, das ich nicht deklariert habe (obwohl ich es sehr wohl deklariert habe).
Der obige Template-ctor ist kein Copy-ctor, zudem ist ja ohnehin nur eine einzige Spezialisierung möglich, wozu diese noch einmal explizit durchführen? Was hast du denn versucht?
- Aus dem Kopierkonstruktor ein Template zu machen ist auch keine Lösung. Die Initialisierung wird ja für den folgenden Fall gebraucht, und da wird AFAIK kein Template korrekt hin aufgelöst, oder?
Ein Templatekonstruktor ist niemals ein Copy-ctor. Wieder kann ich nicht ganz folgen.
mycontainer_type mycont; // ruft die non-const-Version auf. mycontainer_type::const_iterator = mycont.begin();Das sollte (nachdem es korrigiert wurde) eigentlich funktionieren, genau für diesen Fall haben wir uns ja die Mühe gemacht.
… ist aber alles egal, ich habe es jetzt nochmal mit einer Konvertierungsfunktion versucht und das klappt super. Ohne SFINAE, ohne partielle Spezialisierung:
operator list_iterator<const_node_iterator> () const { return list_iterator<const_node_iterator>(m_nodeptr); }Das ist natürlich ebenfalls möglich.
-
camper schrieb:
Konrad Rudolph schrieb:
Übrigens:
- Explizite Template-Spezialisierung des Kopierkonstruktors funktioniert nicht. Ich weiß nicht genau wo der Wurm drin ist. Der Compiler meckert, ich würde ein Member definieren, das ich nicht deklariert habe (obwohl ich es sehr wohl deklariert habe).
Der obige Template-ctor ist kein Copy-ctor
Ja, schon klar. Ich bezog mich auf den Code von queer_boy. Hierbei ist natürlich ebenfalls nur einer der beiden Konstruktoren ein copycon, der andere nicht. Aber wie queer_boy schrieb, müsste der passende copycon dann doch automatisch erstellt werden.
- Aus dem Kopierkonstruktor ein Template zu machen ist auch keine Lösung. Die Initialisierung wird ja für den folgenden Fall gebraucht, und da wird AFAIK kein Template korrekt hin aufgelöst, oder?
Ein Templatekonstruktor ist niemals ein Copy-ctor.
Ja, meinte ich mit „ist auch keine Lösung“. Wobei ich übersehen habe, dass das Template bei diesem Konstruktor in der Tat korrekt aufgelöst wird.