F
starseed84 schrieb:
ich denke es wird tatsächlich auf eine Eigen-Implementierung von einem static_vector<> hinauslaufen. Ich habe mir noch mal den std::vector<> angesehen und selbst mit einem eigenen allocator kommt mir hier das ::new deutlich zu oft vor, das ist für embedded nichts.
Ich hab den Thread zwar nur überflogen, aber ich denke wenn du an diesem Punkt angelangt bist, kann vielleicht auch die SmallVector-Implementierung des LLVM-Projekts eine Inspirationsquelle sein.
Ich bin darauf durch diesen Vortrag aufmerksam geworden. SmallVector ist jedoch kein reiner "statischer Vektor", sondern eine hybride Datenstruktur: SmallVector<T, N> speichert bis zu N Elemente
auf dem Stack, kann jedoch auch größer werden und mutiert dann transparent zu einem dynamischen Array auf dem Heap - ein wenig wie die Small String Optimization die man in diversen std::string -
Implementierungen findet. SmallVector verwendet allerdings eine etwas fragwürdige Methode um ohne Polymorphie, sondern lediglich mit gewolltem Object Slicing nur über die Basisklasse (ohne den
Template-Parameter N ) auf den Vektor zugreifen zu können (nützlich um sich bei Funktionsparametern keinen Kopf darüber machen zu müssen, ob es sich um einen Stack- oder einen Heap-Vektor
handelt). Wenn man ein Auge zudrückt, kann man das (in diesem Fall Chris Lattner, von dem der fragliche Commit stammt) aber noch gerade so durchgehen lassen.
T memory[SIZE];
Was den automatischen Speicher angeht, würde ich allerdings eher folgenden Member empfehlen:
std::aligned_storage_t<sizeof(T), alignof(T)> elements[N];
So ist sichergestellt, dass die Datenstruktur auch mit ungewöhnlicheren Alignments klar kommt. Das Placement- new muss natürlich an den entsprechenden Stellen in einer Schleife für jedes Vektor-Element
aufgerufen werden. Wenn du sogar zulassen willst, dass die Konstruktoren von T Exceptions werfen können, wird es sogar noch etwas umfangreicher: In diesem Fall must du auch sicherstellen, dass wenn
z.B. das 10. Element eine Exception wirft, die ersten 9 Elemente zerstört werden, indem du manuell ihren Destruktor aufrufst. Ein selbstgestrickter Vektor von Bibliotheks-Qualität ist auf jeden Fall etwas
mehr Aufwand, als es auf den ersten Blick den Anschein macht.
starseed84 schrieb:
Hallo Swordfish,
im Endeffekt ist es ein placement new, sprich, das new holt sich vor-allokierten Speicher von einer Adresse (in unserem Fall das char array) und konstruiert hier ein neues Objekt.
Ich verstehe nur nicht ganz wieso ich es benötige schließlich wird ja der Zuweisungsoperator genutzt. Eigentlich sollte dieser ja alles was in Objekt A (an 0xABCD) steht, eben an die Adresse von Objekt B (0xCDEF) in unserem Array kopieren. Ob an der Adresse dann in wirklichkeit ein char array mit wilden Daten liegt dürfte doch egal sein?
Nein, das würde nicht mit beliebigen Objekten funktionieren. Es ist nicht garantiert, dass der Zuweisungsoperator tatsächlich alles kopiert was das Objekt in einen konsistenten Zustand bringt. T könnte
z.B. auch Member haben die spezifisch für die Instanz sind und bei einer Zuweisungsoperation nicht mitkopiert werden - z.B. einen Mutex. Man kann allerdings spezielle Optimierungen implementieren, z.B.
für Objekte, für die std::is_trivially_copyable<T>::value wahr ist. In diesem Fall lassen sich die Objekte mit einem simplen std::memcpy kopieren. Der allgemeingültige Weg ist allerdings über einen
Kopier/Move-Konstruktor, oder einen Default-Konstruktor mit anschliessender Zuweisung.