Ich verstehe die Frage des Benutzers vollkommen. Er/sie möchte eine benutzerdefinierte Containerklasse, die dem Benutzer eine STL-ähnliche Schnittstelle zur Verfügung stellt. Die Standard-STL-Container reichen in gewisser Weise nicht aus und sind daher keine geeignete Wahl.
Ich habe zum Beispiel eine Schnittstellenklasse für eine 'Dataline' namens IDataline. Eine Implementierung der IDataLine-Schnittstelle nimmt bei der Konstruktion eine begrenzte Zeichenkette, parst sie und gibt die Liste der Felder über einen const_iterator mit forward_iterator_tag-Semantik aus. Kein STL-Container kann dies von Haus aus tun.
Außerdem möchte meine Kundenklasse in der Lage sein, durch zwei Datenzeilenfelder zu iterieren und sie Feld für Feld zu vergleichen.
Ich habe die IDataline-Schnittstelle wie folgt definiert:
1 class IDataline
2 {
3 public:
4 class const_iterator
5 {
6 public:
7 virtual const_iterator& operator=(const const_iterator& rhs) =0;
8 virtual bool operator==(const const_iterator& rhs) =0;
9 virtual bool operator!=(const const_iterator& rhs) =0;
10 virtual const_iterator& operator++() =0;
11 virtual const_iterator operator++(int) =0;
12 virtual const Field& operator*() =0;
13 virtual const Field* operator->() =0;
14 virtual const Field& operator[](size_t idx) =0;
15 virtual size_t offset() =0;
16 };
17
18 virtual const_iterator begin() =0;
19 virtual const_iterator end() = 0;
20 };
Das Problem ist in den Zeilen 11, 18 und 19 zu sehen - wir müssen in der Lage sein, einen const_iterator zurückzugeben, was einen Kopierkonstruktor erfordert, aber da es sich um eine virtuelle Schnittstelle handelt, gibt es weder einen Standard- noch einen Kopierkonstruktor (für den Schnittstellentyp), und der Compiler versagt (ordnungsgemäß).
Sie könnten argumentieren, dass ich begin() und end() als definieren könnte:
virtual const_iterator& begin() = 0;
virtual const_iterator& end() = 0;
Dann kann ich zwei Instanzen des spezialisierten Iterators im spezialisierten Host erstellen und Referenzen (oder Zeiger für diejenigen, die diese bevorzugen) zurückgeben, um dies für meinen Fall zu erreichen.
Das Problem ist, dass eine solche Implementierung nicht alle Anforderungen für Vorwärts-Iteratoren erfüllt und in allgemeineren Anwendungsfällen, die auf die Semantik von Vorwärts-Iteratoren angewiesen sind, nicht funktioniert.
Nach einigem Nachdenken (und der Beratung durch einen Kollegen) bin ich auf zwei Möglichkeiten gestoßen, wie dieses Problem gelöst werden kann:
-
Muss Ihre Gastgeberklasse sein vollständig abstrakt? Wenn Sie die Abstraktion auf das Verhalten beschränken können, das Sie variieren müssen, dann kann Ihr instanziierbarer Host einen konkreten Iterator einbetten (weil eine Instanz der Klasse selbst instanziiert werden kann) und Sie haben, was Sie brauchen.
-
Eduardo Leon zeigt an, dass der Iterator selbst mit dem Idiom pimpl (Pointer to an implementation) umhüllt werden kann. Es gibt zwar eine Menge Material, das diese Technik ausführlich beschreibt, Basilev , in den Kommentaren unten Leons deutet darauf hin, dass er nicht glaubt, dass das Idiom pimpl funktionieren wird. Mein Kollege bot mir eine Technik zum Ausprobieren an, also werde ich ein Testmuster ausarbeiten. Wenn es funktioniert, werde ich es teilen. Wenn nicht, werde ich die Erfahrungen, die ich mit dem Beispiel gemacht habe, detailliert beschreiben und darauf warten, dass jemand, der mehr Erfahrung hat, sich dazu äußert, ob pimpl in diesem Fall anwendbar ist oder nicht.