Wie sicher ist vector.insert bei einem zu großen index?



  • Hi,

    Ich habe mir eine komfortable Klasse geschrieben, womit ich einen unsigned char Vector leicht füllen kann. Nun stellt sich mir die Frage, was der schnellste und sicherste weg bei folgendem Code ist:

    template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
    Proxy& operator+=(T const& value)
    {
    	this->parent.data.insert(this->parent.data.begin() + this->index, sizeof(T), 0x00);
    
    	std::memcpy(&this->parent.data[this->index], &value, sizeof(T));
    
    	return *this;
    }
    
    Data data = Klasse();
    
    data[4] += (short)0x12;
    

    Das ganze ist wie gesagt eine Helferklasse in Data. Es wird auch ordnungsgemäß ausgerufen. Aber meine frage ist jetzt, ob es bei dem insert Probleme gibt, wenn begin() + index größer als vector.size ist? An sich ging das bis jetzt immer gut, nur das heißt ja noch nichts. Wenn der Index wirklich zu groß ist, soll mit 0x0 aufgefüllt werden bis zum index.

    An anderer stelle prüfe ich expliziert weil es direkt Probleme gab (wobei ich nicht sicher bin ob das der beste weg ist)

    if (this->index >= this->parent.data.size()) {
    	this->parent.data.resize(this->index + value.size());
    }
    

    Wenn gewünscht Poste ich die gesamte Klasse mal, denke das auch andere das mal gebrauchen könnten.


  • Mod

    MrSpoocy schrieb:

    Aber meine frage ist jetzt, ob es bei dem insert Probleme gibt, wenn begin() + index größer als vector.size ist?

    Bereits die Addition führt zu undefiniertem Verhalten. Wieso eigentlich der Umweg über 0-Initialisierung+memcpy? Wäre nicht

    if (parent.data.size() < index) {
        parent.data.reserve(index+sizeof(value));
        parent.data.resize(index):
    }
    auto& arr = reinterpret_cast<const char (&arr)[sizeof(value)]>(value);
    parent.data.insert(parent.data.begin() + index, std::begin(arr), std::end(arr));
    

    naheliegender und effizienter?

    Edit: richtig:

    if (parent.data.size() < index) {
        parent.data.reserve(index+sizeof(value));
        parent.data.resize(index):
    }
    auto& arr = reinterpret_cast<const char (&)[sizeof(value)]>(value);
    parent.data.insert(parent.data.begin() + index, std::begin(arr), std::end(arr));
    


  • Ich bin mir ja nicht sicher was effizienter ist.

    Ist es sinnvoll sizeof(value) und nicht T zu machen?
    Und warum reserve vor dem resize? Macht das überhaupt sinn?

    Aber irgendwas stimmt eh nicht ganze mit dem Code. Er gibt einen Fehler aus das eine ) beim reinterpret_cast fehlt. (Den falschen : hab ich gegen ; schon ersetzt).

    Ich muss zugeben das diese Syntax von reinterpret_cast nicht bekannt ist



  • MrSpoocy schrieb:

    Aber irgendwas stimmt eh nicht ganze mit dem Code. Er gibt einen Fehler aus das eine ) beim reinterpret_cast fehlt. (Den falschen : hab ich gegen ; schon ersetzt).

    Ich muss zugeben das diese Syntax von reinterpret_cast nicht bekannt ist

    Die Syntax die dich hier wahrscheinlich verwirrt ist nicht spezifisch für reinterpret_cast sondern der Typ in den gecastet wird:
    Hierbei handelt es sich um eine Referenz auf ein Array fester Grösse - sowas sieht man relativ selten, weil hierfür oft mit Pointern gearbeitet wird und "man die Grösse eben weiss".
    C++ kennt aber auch diese schöne Syntax mit der die Grösse des Arrays als Teil des Typs erhalten bleibt:

    const char (&arr)[4]

    ist eine Referenz ( T& ) auf ein const char[4] -Array mit dem Namen "arr". Hier liegt übigens auch der Fehler:
    In den Typ-Templateparameter beim Cast gehört kein Variablenname ( arr in diesem Fall), daher muss die Zeile lauten:

    auto& arr = reinterpret_cast<const char (&)[sizeof(value)]>(value);

    ... das sollte den Syntaxfehler beheben.

    Dass die Array-Grösse (in diesem Fall sizeof(value) ) nicht wie bei einem const char* -Cast einfach weggeworfen wird, sondern als Teil des Variablen-Typs erhalten bleibt,
    ist übrigens auch der Grund weshalb man in der nächsten Zeile bequem mit std::begin(arr) und std::end(arr) arbeiten kann, da der Compiler bereits "weiss" welche Länge das Array hat.

    Ich empfehle dir, die Syntax für Referenzen und Pointer auf Arrays fester Länge mal genauer anzusehen, auch wenn sie etwas exotisch aussieht (wie Funktions-Pointer) - es gibt
    immer mal wieder (wenn auch seltene) Anwendungsfälle wo es sich lohnt eine solche compile-time-konstante Array-Grösse als Typ mitzuziehen - sei es weil einen schon der Compiler
    (und nicht erst der Debugger) auf einen ungültigen Array-index hinweisen kann, oder um sich einen zusätzlichen "length"-Parameter zu sparen.

    Gruss,
    Finnegan


  • Mod

    MrSpoocy schrieb:

    Und warum reserve vor dem resize?

    Nach dem resize kommt ja noch das insert. Da wir wissen, um wieviel der vector größer wird, können wir hier eine evtl. nötige zweite Reallokation wenigstens vorläufig vermeiden.



  • Was sagt ihr zu:

    parent.data.insert(parent.data.end(), reinterpret_cast<const unsigned char*>(&value), reinterpret_cast<const unsigned char*>(&value) + sizeof(value));
    

    ?

    @camper, aber genau das wundert mich ja, wir wissen ja die genaue Größe und passen bei resize die Größe bereits genau an. Also wird es doch keine 2te allokation geben oder sehe ich da etwas falsch?


Anmelden zum Antworten