Eigener Container möglichst STL konform



  • Ich habe eine eigene Ringpuffer-Implementierung geschrieben, auf welchen man auch mittels Range-Based Loops zugreifen kann.

    Da ich dies zum ersten Mal gemacht habe, bin ich noch ein wenig unsicher bezüglich den diversen using Anweisungen im Code. Aktuell betrifft dies die folgende Stelle:

    template<typename T, size_t N>
    class CircularBuffer
    {
    public:
      using value_type = T;
      using pointer = T*;
      using const_pointer = T const* const;
      using reference = T&;
      using const_reference = const T&;
      using size_type = size_t;
    
    public:
      class Iterator
      {
      public:
        using iterator_category = std::input_iterator_tag;
        using value_type = T;
        using difference_type = int;
        using pointer = value_type const* const;
        using reference = const value_type&;
      // ...
      }
    // ...
    }
    

    Die using Anweisungen habe ich aus einen Internet-Beispiel genommen und an die MS Implmentierung von std::vector angepasst.

    Aber wozu braucht man diese?

    Die using iterator_category Anweisung verstehe ich beispeislweise, da dies fürs Tag Dispatching dient. Ähnliches kann ich mir bei value_type vorstellen.

    Aber wozu beispielsweise die using pointer Anweisung?


  • Mod

    So etwas kann für relativ triviale Dinge nützlich sein, wenn du 1998 eine generische Bibliothek schreiben möchtest, aber das auto-Schlüsselwort, wie wir es heute kennen, oder decltype erst 13 Jahre später kommen.



  • auto verwirft einige Eigenschaften, so dass der Rückgabetyp nicht zu 100% dem entspricht, was auto an Typ erzeugt. Die using Anweisungen sind für den Fall gedacht, dass man 100% korrekte Typen braucht.

    Was bei Quiche Lorraine komplett fehlt ist die korrekte Umsetzung der Allokatorunterstüzung, die bei STL-Container Pflicht ist. Dann sind einige der Using Anweisungen nicht korrekt, da man einiges nicht direkt formulieren sollte, sondern über die Type Traits Templates ableiten lassen sollte.

    Nachtrag:
    Ich habe mein Matrix Beispiel endlich mal gepostet, so dass man sehen kann wie man Allokatoren nutzen kann.



  • @john-0 sagte in Eigener Container möglichst STL konform:

    Ich habe mein Matrix Beispiel endlich mal gepostet, so dass man sehen kann wie man Allokatoren nutzen kann.

    Danke schön. 👍 Schaue ich mir an.



  • @SeppJ
    Ich habe einen weiteren Einsatzzweck für die using Anweisungen gefunden.

    Und zwar wollte ich für meinen Ringpuffer auch einen const Iterator implementieren. Auf Stack Overflow fand ich den Tip mittels std::conditional. Also habe ich folgendes gemacht:

    // Demo Code, nicht lauffähig oder vollständig
    
    template<typename T, size_t N>
    class CircularBuffer
    {
    public:
      template<typename BaseType, typename ContainerType, typename SizeType, bool IsConst>
      class IteratorBase
      {
      public:
        // ...
        using value_type = BaseType;
        using reference = typename std::conditional<IsConst, value_type const&, value_type&>::type;
        using container_type_pointer = typename std::conditional<IsConst, ContainerType const*, ContainerType*>::type;
        using container_type_reference = typename std::conditional<IsConst, ContainerType const&, ContainerType&>::type;
    	
      private:
        container_type_pointer mBuffer;
        SizeType mPos = 0;
        SizeType mSize = 0;
    	
      public:
        IteratorBase(container_type_reference Buffer, SizeType Pos, SizeType Size) :
          mBuffer(&Buffer),
          mPos(Pos),
          mSize(Size)
        {  
        }
    	
        IteratorBase::reference operator*()
        {
           container_type_reference Ref = *mBuffer;
    		
          return Ref[mPos];
        }	
        //...
      };
      
      using ConstIterator = IteratorBase<T, std::array<T, N>, size_t, true>;
      using Iterator = IteratorBase<T, std::array<T, N>, size_t, false>;
      
      // ...
      
      Iterator begin()
      {
        return Iterator(mValues, mReadIndex, mSize);
      }
      
      
      ConstIterator begin() const 
      {
        return ConstIterator(mValues, mReadIndex, mSize);
      }
    };
    

    Das finde ich stellenweise nicht schön, ist aber in der Praxis selten sichtbar und vor allen Dingen ist der Code schön kompakt.



  • @Quiche-Lorraine sagte in Eigener Container möglichst STL konform:

        using container_type_pointer = typename std::conditional<IsConst, ContainerType const*, ContainerType*>::type;
        using container_type_reference = typename std::conditional<IsConst, ContainerType const&, ContainerType&>::type;
    

    Würde ich nicht public machen. Und ich würde die Bezeichner vermutlich auch nicht in "schaut-nach-Standard-aus" snake_case schreiben. Bzw. nur wenn du die Benamsung auch für eigenen Code verwendest -- aber da scheinst du ja eher PascalCase für Typen zu verwenden.

       IteratorBase::reference operator*()
    

    Das IteratorBase:: ist redundant.



  • @hustbaer
    Danke für die Tipps, habe ich nachgezogen.


Anmelden zum Antworten