Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > de.comp.lang.iso-c++ > #2091 > unrolled thread

std::string und eigener allocator

Started byThomas Dorner <td-dclic01@th-dorner.de>
First post2018-11-08 15:47 +0100
Last post2018-11-12 12:25 +0100
Articles 3 — 3 participants

Back to article view | Back to de.comp.lang.iso-c++


Contents

  std::string und eigener allocator Thomas Dorner <td-dclic01@th-dorner.de> - 2018-11-08 15:47 +0100
    Re: std::string und eigener allocator ram@zedat.fu-berlin.de (Stefan Ram) - 2018-11-08 18:49 +0000
    Re: std::string und eigener allocator Markus Schaaf <mschaaf@elaboris.de> - 2018-11-12 12:25 +0100

#2091 — std::string und eigener allocator

FromThomas Dorner <td-dclic01@th-dorner.de>
Date2018-11-08 15:47 +0100
Subjectstd::string und eigener allocator
Message-ID<6esh0b8w2x.fsf@th-dorner.de>
Hallo C++ Speziallisten,

ich habe vermutlich ein Verständnisproblem bei der Nutzung der Standard
C++ String Klasse in Verbindung mit einem eigenen allocator.  Das Ziel
ist, bestimmte Strings gesondert im Speicher abzulegen, und dabei
ansonsten völlig kompatibel zu den "normalen" Strings zu sein.  Mein
minimales Beispiel:

#include <iostream>

class myalloc : public std::allocator<char>
{
    template<typename T>
    T* allocate(std::size_t n, const void* = 0)
    {
        std::cout << "myalloc::allocate(" << n << ")\n";
        if(100 < n) throw std::bad_alloc();
        return static_cast<T*>(&_memory);
    }
private:
    unsigned char _memory[100];
};
myalloc customAllocator;

int main()
{
    std::string mystring(customAllocator);
    std::string regularString = "test string\n";
    mystring = regularString;
    std::cout << mystring;
}

Sowohl GCC (als auch Clang und zapcc auf Wandbox (https://wandbox.org/))
akzeptieren den Code, rufen aber nie die spezielle allocate Methode auf,
sondern nach wie vor die von std::allocator.

Beim Versuch, das Verhalten zu verstehen, bin ich in den Tiefen der GCC
C++ Standardbibliothek (bits/basic_string.tcc, in verschiedenen GCC 4er
und 6er Versionen) über folgende Zeile gestolpert:

      void* __place = _Raw_bytes_alloc(__alloc).allocate(__size);

"__alloc" dürfte hier noch mein Allocator sein, aber "_Raw_bytes_alloc "
ist ein typedef des std::allocator in bits/basic_string.h:

typedef typename _Alloc::template rebind<char>::other _Raw_bytes_alloc;

"rebind<char>::other" ist wiederum mehr oder weniger ein typedef auf
sich selbst in bits/allocator.h:

      template<typename _Tp1>
        struct rebind
        { typedef allocator<_Tp1> other; };
    };

Für mich liest sich das so, als ob die Standard C++ String Klasse des
GCC hier vor dem Aufruf der "allocate" Methode einen expliziten Cast auf
die Klasse des Allocator der Datentypdefinition machen, anstatt die
speziell angegebene zu verwenden.

Ist dieses Verhalten standard-konform?
Wenn ja, warum; wo liegt mein Denkfehler?

Danke schon mal im Voraus für die Erklärung,

[toc] | [next] | [standalone]


#2092

Fromram@zedat.fu-berlin.de (Stefan Ram)
Date2018-11-08 18:49 +0000
Message-ID<String-Allokator-20181108194619@ram.dialup.fu-berlin.de>
In reply to#2091
Thomas Dorner <td-dclic01@th-dorner.de> writes:
>T* allocate(std::size_t n, const void* = 0)

  Hier könnte man statt »= 0« auch schreiben: »= nullptr«.

>std::string mystring(customAllocator);

  Nach dieser Zeile bestätigt ein

::std::cout <<( mystring.get_allocator() == customAllocator )<< '\n';

  , daß der Spezialallokator im Prinzip angekommen ist.

  Aber stackoverflow.com:

|std::string is a typedef of basic_string that already
|explicitly uses the default allocator. There is no way for
|std::string to use a different allocator.
"Mark B"?

  (std::string ist ein typedef von basic_string, die bereits
  explizit den Standardallokator verwendet. Es gibt keine
  Möglichkeit, für std::string, einen anderen Allokator zu
  verwenden.)

  , cplusplus.com:

|All constructors above support an object of member type
|allocator_type as additional optional argument at the end,
|which for string is not relevant

  (Alle Konstruktoren oben unterstützen ein Objekt vom Typ
  allocator_type als zusätzliches optionales Argument am Ende,
  das für ::std::string nicht relevant ist.)

  . Dementsprechend wird meist von »basic_string« ausgegangen,
  stackoverflow.com:

|typedef basic_string<char, char_traits<char>,
|my_allocator<char> > my_string;
"GManNickG"?

  .

[toc] | [prev] | [next] | [standalone]


#2093

FromMarkus Schaaf <mschaaf@elaboris.de>
Date2018-11-12 12:25 +0100
Message-ID<psbnvu$r5o$1@dont-email.me>
In reply to#2091
Am 08.11.18 um 15:47 schrieb Thomas Dorner:

> #include <iostream>
> 
> class myalloc : public std::allocator<char>
> {
>     template<typename T>
>     T* allocate(std::size_t n, const void* = 0)

Die "Vererbung" ist irreführend. std::allocator<> enthält keine
virtuellen Funktionen. Damit kann es keine Basisklasse im Sinne
objekt-orientierter Programmierung sein. Deshalb würde in vielen
Programmier-Richtlinien eine Verwendung als öffentliche Basisklasse
nicht gestattet. (Eine Ausnahme wäre das "Curiously recurring template
pattern".) (Wenn man nur Funktionen wiederverwenden möchte, würde man
privat vererben und Using-Deklarationen benutzen.)

>     {
>         std::cout << "myalloc::allocate(" << n << ")\n";
>         if(100 < n) throw std::bad_alloc();
>         return static_cast<T*>(&_memory);
>     }
> private:
>     unsigned char _memory[100];
> };
> myalloc customAllocator;
> 
> int main()
> {
>     std::string mystring(customAllocator);

Hier passiert sog. Slicing. Der std::allocator<>-Teil wird kopiert. Das
Verhalten ist danach natürlich das übliche. Dass dieser ganz normale
std::allocator<> durch Kopie eines myalloc entstanden ist, ändert sein
Verhalten nicht. Diese Art Fehler ist übrigens die Motivation für o.g.
Richtlinien.

MfG

[toc] | [prev] | [standalone]


Back to top | Article view | de.comp.lang.iso-c++


csiph-web