2016-08-02 4 views
5

Es gibt verschiedene STL-Algorithmen, die auf einem Ausgabe-Iterator beruhen, um das Ergebnis des Algorithmus zu speichern.Ausgabe-Iterator-Adapter zum Zählen, aber nicht zum Kopieren

Zum Beispiel speichert std::set_intersection alle gemeinsamen Elemente zwischen zwei sortierten Bereichen in einem Ausgabe-Iterator, der dann nach jedem ausgegebenen Element inkrementiert wird.

Manchmal interessiert mich nicht die tatsächlichen Elemente, sondern nur die Anzahl der Ausgabeelemente. In solchen Fällen ist es eine Verschwendung von Speicher und Leistung, die Elemente zu kopieren. Gibt es einen Iteratoradapter, mit dem ich die Kopie der Elemente zählen und vermeiden kann? Wenn nicht, können Sie eine generische Implementierung eines solchen Adapters vorschlagen?

+0

Voting als Ressourcenanforderung zu schließen, aber Sie möchten [ 'boost :: counting_iterator'] (http://www.boost.org/doc/libs/1_50_0/libs/iterator/doc/counting_iterator. html) – NathanOliver

+0

Ihre Frage ist ein wenig unklar: Sie möchten wissen, wie viele Elemente die Schnittmenge von 2 Sätzen ohne Berechnung der Schnittmenge enthalten würde? – wasthishelpful

+0

@washishelpful ja, in meinem Beispiel von set_difference, aber ich bin auf der Suche nach einer generischen Ausgabe Iterator-Lösung für jeden solchen Algorithmus. – T33C

Antwort

0

Dank viel Hilfe von @ecatmur Antwort und Kommentare, ich die folgende Lösung, die ich Kommentare einladen. Ich hatte gehofft, dass die boost::make_function_output_iterator funktioniert, aber es scheint, dass es einen Fehler in der Bibliothek gibt, der den Zuweisungsoperator nicht definiert.

#include <algorithm> 
#include <vector> 
#include <iostream> 
#include <string> 
#include <cassert> 

class counting_output_iterator 
{ 
public: 
    counting_output_iterator& operator=(const counting_output_iterator&) { return *this; }; 
    explicit counting_output_iterator(std::size_t& count) : m_count(count) {} 
    template<typename T> void operator=(const T&) {}; //NULL op 
    using iterator_category = std::output_iterator_tag; 
    using value_type = void; 
    using difference_type = void; 
    using pointer = void; 
    using reference = void;  
    counting_output_iterator& operator*() { return *this; } 
    counting_output_iterator& operator++() { ++m_count; return *this; } 
    std::size_t& m_count; 
}; 

int main(int, char*[]) 
{ 
    std::vector<int> arr{ 1,2,3,4 }; 
    std::size_t count = 0; 
    std::copy(std::begin(arr), std::end(arr), counting_output_iterator{ count }); 
    assert(count == 4u); 
    return 0; 
} 
4

Erhöhung des Function Output Iterator können tun, was Sie wollen:

std::size_t count = 0u; 
int arr[]{0, 1, 2, 3}; 
std::copy(std::begin(arr), std::end(arr), 
    boost::make_function_output_iterator([&](auto const&) { ++count; })); 
assert(count == 4u); 

Das einzige Problem ist, dass Sie die Zählvariable außerhalb des Iterators zu erklären, weil es keine Möglichkeit gibt, die gespeicherte Funktion Objekt aus einem boost::function_output_iterator zu extrahieren (und auch keine Möglichkeit, die Schlusswerte aus einem Lambda zu extrahieren, selbst wenn Sie diese Hürde überschritten haben). Wenn Sie in der Lage sein möchten, Einzeiler zu schreiben, müssen Sie die Iteratorklasse selbst schreiben, aber es ist keine große Menge Code; zum Beispiel:

class counting_output_iterator { 
public: 
    using iterator_category = std::output_iterator_tag; 
    using value_type = void; 
    using difference_type = void; 
    using pointer = void; 
    using reference = void; 

    std::size_t value = 0u; 

    struct output_proxy { 
    output_proxy(std::size_t& value) : m_value(value) { } 
    template<class T> output_proxy& operator=(T const&) { 
     ++m_value; 
     return *this; 
    } 
    std::size_t& m_value; 
    }; 
    output_proxy operator*() { return output_proxy(value); } 
    counting_output_iterator& operator++() { return *this; } 
    counting_output_iterator& operator++(int) { return *this; } 
}; 

Verbrauch:

int arr[]{0, 1, 2, 3}; 
auto const count = std::copy(std::begin(arr), std::end(arr), 
    counting_output_iterator{}).value; 
assert(count == 4u); 
+0

Sieht vielversprechend aus, aber Code kompiliert nicht in VS2015U2 mit Boost 1.59. Versuch, eine gelöschte Funktion zu referenzieren. Ich ersetzte mit einem Funktor, der kompiliert aber 6 statt 3 zählt. Ich untersuche immer noch. Danke für Ihren Vorschlag. – T33C

+0

Sie haben wesentliche Änderungen vorgenommen, da ich diese zuerst kommentiert habe, was meinen obigen Kommentar ungültig machen könnte. Danke für Ihre Hilfe. – T33C

+0

Lambda-Lösung schlägt immer noch fehl. Der counting_output_operator funktioniert, hängt aber davon ab, dass der Algorithmus den Ausgabe-Iterator zurückgibt, andernfalls ist der Wert nicht zugänglich. Sie haben mich auf den richtigen Weg gebracht und ich werde mehr experimentieren. – T33C

Verwandte Themen