2017-05-17 2 views
4

Betrachten Sie zum Beispiel einige hypothetische to_upper_iterator, die über einen Bereich von Zeichen iteriert, std::toupper für operator* zurückgeben. Diese Iterator Aliase macht Sinn für mich:Was für `Iterator :: pointer` zu verwenden, wenn nichts Sinn macht?

template <typename CharT> 
struct to_upper_iterator 
{ 
    using value_type = CharT; 
    using reference = CharT; 
    using difference_type = std::ptrdiff_t; 
    using iterator_category = std::random_access_iterator_tag; 
}; 

Was nicht sinnvoll ist, was könnte/sollte für den pointer Alias ​​verwendet werden. Ich habe versucht, es wegzulassen, aber sicher genug, ich habe Kompilierungsfehler. Es scheint, dass dies auf eine Addition in C++ 17 zurückzuführen ist. von en.cppreference.com für den std::iterator_traits Typ Zusammengefasst:

Wenn Iterator nicht über die fünf Elementtypen difference_type, value_type, pointer, reference und iterator_category, dann hat diese Vorlage keine Mitglieder von jedem dieser Namen (std::iterator_traits ist SFINAE- freundlich)

die Frage ist also: für Typen wie diese, sollte ich pointer-etwas nur definieren - mein führende Favorit sein void oder void* - oder würde es mehr Sinn machen, etwas wie Spezialisierung std::iterator_traits<to_upper_iterator> zu tun, so dass es keinen Alias ​​für pointer enthält.

Antwort

4

Die Standard-C++ - Modelle von OutputIterator definieren alle void.

Beispiel: http://en.cppreference.com/w/cpp/iterator/back_insert_iterator

wäre ich geneigt, das gleiche gilt für jene Art zu tun, die keine Bedeutung im Zusammenhang mit Ihrer Iteratortyp haben.

Dieser Iterator von Ihnen sieht aus wie es das Konzept eines Adapters modelliert. In diesem Fall könnte ich geneigt sein, in diese Richtung zu starten:

#include <algorithm> 
#include <string> 
#include <iterator> 
#include <utility> 
#include <iostream> 

template <typename BaseIter> 
struct to_upper_iterator 
{ 
    using value_type = typename std::iterator_traits<BaseIter>::value_type; 
    using reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>; 
    using pointer = std::add_pointer_t<std::add_const_t<value_type>>; 
    using difference_type = typename std::iterator_traits<BaseIter>::difference_type; 
    using iterator_category = typename std::iterator_traits<BaseIter>::iterator_category; 

    to_upper_iterator(BaseIter iter = BaseIter()) : iter_(iter) {} 

    value_type operator*() const { return std::toupper(*underlying()); } 
    to_upper_iterator& operator++() 
    { 
     iter_++; 
     return *this; 
    } 

    to_upper_iterator operator++(int) 
    { 
     auto copy = *this; 
     iter_++; 
     return copy; 
    } 

    bool operator!=(const to_upper_iterator& other) const { 
     return iter_ != other.iter_; 
    } 

    // etc. use enable_if to enable functionality depending on iterator_category 

private: 
    BaseIter& underlying() { return iter_; } 
    BaseIter const& underlying() const { return iter_; } 

    BaseIter iter_; 
}; 

template<class Iter> 
auto make_upper_iterator(Iter iter) 
{ 
    return to_upper_iterator<Iter>(iter); 
} 

int main() 
{ 
    std::string a = "abcdef"; 

    auto first = make_upper_iterator(a.begin()); 
    auto last = make_upper_iterator(a.end()); 

    std::copy(first, last, std::ostream_iterator<char>(std::cout)); 
    std::cout << std::endl; 

    const char b[] = "abcdef"; 
    std::copy(make_upper_iterator(std::begin(b)), 
     make_upper_iterator(std::end(b) - 1), 
     std::ostream_iterator<char>(std::cout)); 
    std::cout << std::endl; 
} 
+1

Die Frage beschreibt keinen Ausgabe-Iterator. Ein Ausgabe-Iterator ist eine reine Senke. Was die Frage beschreibt, ist ein transformierender Iteratoradapter. – Johan

+1

@Johan rechts. Ich habe den Extremfall gewählt, um den Punkt zu demonstrieren. Vielleicht wäre ein besseres Modell in diesem Fall 'istream_iterator <>' was den Zeiger als 'const CharT *' definiert –

2

to_upper_iterator::pointer sollte eine Art sein, die auf die value_type zeigen kann und markieren, dass Sie den Wert nicht ändern können Sie es const machen sollte, das heißt const CharT*

+0

Worauf sollte es zeigen? (Es gibt eine vernünftige Antwort, aber es verändert das Design des Iterators erheblich) –

+0

@ JonathanWakely Es sollte natürlich ein Typ sein. Sloppy schreibe in meinem Namen. – Johan

+1

Das ist nicht was ich meinte. Wenn es ein Zeiger vom Typ 'const CharT *' ist, worauf deutet es hin? was ist sein Wert? Wenn es null ist, ist es nicht sehr nützlich, und wenn es nicht null ist, muss es auf etwas zeigen. –

0

Meine eigene Antwort hinzufügen, aber vorausgesetzt, dass SO mich nicht zwingt, wird es nicht als die Antwort markieren. Es scheint, als ob die entsprechende Formulierung in § ist 27.4.1

27.4.1 Iterator Züge [iterator.traits]

Um Algorithmen nur in Bezug auf Iteratoren zu implementieren, ist es oft notwendig, um zu bestimmen, der Wert und die Differenz Typen, die einem bestimmten Iteratortyp entsprechen. Dementsprechend ist es erforderlich, dass, wenn Iterator ist die Art der ein Iterator, die Typen

iterator_traits<Iterator>::difference_type 
iterator_traits<Iterator>::value_type 
iterator_traits<Iterator>::iterator_category 

als Differenz Typ des Iterators definiert werden, und Werttyp Iterator Kategorie, respectively.Darüber hinaus sind die Typen

iterator_traits<Iterator>::reference 
iterator_traits<Iterator>::pointer 

sind als Referenz und Zeigertypen des Iterators definiert werden, das heißt, für ein Iterator-Objekt a, vom gleichen Typ wie die Art der *a und a->, respectively. In dem Fall eines Ausgangs-Iterator, die Typen

iterator_traits<Iterator>::difference_type 
iterator_traits<Iterator>::value_type 
iterator_traits<Iterator>::reference 
iterator_traits<Iterator>::pointer 

kann als void definiert werden.

In meinem Fall ist operator-> nicht sinnvoll, da ich Charaktertypen Rückkehr gerade bin, so kann ich wohl mit void wegkommen als @ Richard Hodges vorgeschlagen. Ich nehme an, dass die "richtige" Antwort im allgemeinen Fall ist, irgendeinen pointer_proxy<T> Typ zu definieren (vorausgesetzt, dass ein solches Ding nicht bereits in der STL existiert), der eine T enthält und eine operator-> definiert, die T* zurückgibt und diese für den pointer Typ des Iterators verwendet

Verwandte Themen