Warum ist std::valarray so spartanisch? [Wünsche für den nächsten C++-Standard]



  • Hallo,

    ich habe vor kurzem etwas mit std::valarray gemacht und fand es schade und ärgerlich, dass valarray ein relativ kleines Interface anbietet. Zum Beispiel fehlen sowohl Iteratoren für std::valarray wie auch ein Konstruktor mit Iteratoren, push_back() (ok, ich habe gelesen, valarray sei auf feste Größen optimiert), evtl. eine subarray()-Funktion (wie substr()). Bei der sum()-Funktion würde ich mir eine Funktion mit Template-Rückgabetyp wünschen (bspw. array.sum<int>()), da ich sonst std::count(&array[0], &array[0]+array.size(), true) verwenden muss, um bool'sche Werte im Array zu zählen. (sum() bei einem std::valarray<bool> gibt einen bool zurück und daher maximal 1)

    Außerdem interessiert mich, was ihr zu einer abstrakten Basisklasse für Arrays halten würdet (oder mehreren) oder zumindest ähnlicherem Design bspw. für std::basic_string und std::vector. Wobei ich zugeben muss, dass ein std::vector<Type> * dann plötzlich wegen der virtuellen Funktionen langsamer wäre, daher käme wohl eher ein angepasstes Design als eine abstrakte Basisklasse in Frage.

    Übrigens: Was ich auch noch vermisse, ist eine append()-Funktion für std::vector, wenn man schon die Operatoren + und += wegen Mehrdeutigkeit nicht überladen will. Ein insert() mit Iteratoren ist (insbesondere für rvalue-Vektoren) doch etwas lästig.

    P.S.: std::valarray findet sich in der C++-Referenz unter Miscellaneous und nicht unter Containers - das wundert mich doch etwas.



  • wxSkip schrieb:

    ich habe vor kurzem etwas mit std::valarray gemacht und fand es schade und ärgerlich, dass valarray ein relativ kleines Interface anbietet.

    Ja, std::valarray ist etwas speziell. Ich wollte es auch schon verwenden, habe mich aber an der festen Grösse gestört. Der Container scheint wirklich nur für wenige Spezialfälle konzipiert zu sein. Wahrscheinlich waren die Einschränkungen ursprünglich mit Performanceüberlegungen verbunden, allerdings stellt sich die Frage, wie aktuell diese heute noch sind.

    Aber die meisten deiner Features kannst du mit globalen Funktionen implementieren, sogar relativ effizient.

    Siehe auch zwei Stackoverlow-Threads:
    What does the future of std::valarray look like?
    C++ valarray vs. vector

    wxSkip schrieb:

    Außerdem interessiert mich, was ihr zu einer abstrakten Basisklasse für Arrays halten würdet

    Nichts. Was sollte das bringen? Es war die richtige Entscheidung, statische und nicht dynamische Polymorphie zur Implementierung der Container, Iteratoren und Algorithmen zu verwenden.

    wxSkip schrieb:

    Übrigens: Was ich auch noch vermisse, ist eine append()-Funktion für std::vector, wenn man schon die Operatoren + und += wegen Mehrdeutigkeit nicht überladen will. Ein insert() mit Iteratoren ist (insbesondere für rvalue-Vektoren) doch etwas lästig.

    Schreib dir eine freie Funktion. Mir hat insert() bisher immer gereicht, wenn ich mehr auf einmal einfügen wollte.

    wxSkip schrieb:

    Wünsche für den nächsten C++-Standard

    Damit kommst du definitiv zu spät.



  • Nexus schrieb:

    wxSkip schrieb:

    Übrigens: Was ich auch noch vermisse, ist eine append()-Funktion für std::vector, wenn man schon die Operatoren + und += wegen Mehrdeutigkeit nicht überladen will. Ein insert() mit Iteratoren ist (insbesondere für rvalue-Vektoren) doch etwas lästig.

    Schreib dir eine freie Funktion. Mir hat insert() bisher immer gereicht, wenn ich mehr auf einmal einfügen wollte.

    Klar kann ich mir eine freie Funktion schreiben. Habe ich auch schon gemacht. Allerdings gibt es viele Standard-Funktionen, für die ich mir eine freie Funktion schreiben könnte.

    Nexus schrieb:

    wxSkip schrieb:

    Wünsche für den nächsten C++-Standard

    Damit kommst du definitiv zu spät.

    Ich meinte natürlich den Übernächsten. Außerdem ist das ganze Wünschespiel ja sowieso eher ein Wünschespiel. 😉



  • std::valarray ist nicht als Container gedacht und findet sich deshalb auch im Standard nicht in der "Containers library", sondern in der "Numerics library" - es geht hier gedanklich nicht darum, etwas aufzubewahren, sondern um Algebra. std::valarray versteht sich dementsprechend mehr als ein Vektor im mathematischen Sinne, und an einen Vektorraum pappt man in der Regel nicht einfach neue Dimensionen ran.

    Das Ganze versucht so eine Art rudimentäres BLAS bzw. für derartige Systeme nützliche, effiziente Basiswerkzeuge zur Verfügung zu stellen. Es macht in Verbindung mit den anderen (weniger bekannten) Klassen bzw. Klassenvorlagen aus dieser Bibliothek Sinn - slice, mask_array etc. In die Reihe von deque, vector und list passt valarray dagegen nicht.



  • @seldon: Was spricht dagegen, std::valarray sowohl als Container als auch als mathematisches Objekt nutzen zu können? Ich denke nicht, dass Iteratoren Performanceprobleme mit sich bringen würden.



  • wxSkip schrieb:

    @seldon: Was spricht dagegen, std::valarray sowohl als Container als auch als mathematisches Objekt nutzen zu können?

    Es sind zwei völlig unterschiedliche Konzepte. Es bringt nichts, mit dem Trecker auf Rallye fahren zu wollen, genausowenig bringts was, mit dem Rallye-Auto einen pflug zu ziehen. Selbst wenn Rallye-Streifen einen Trecker nicht schwächer machen würden und eine Kupplung für landwirtschaftliche Geräte den Rallye-wagen nicht merklich verlangsamen würden.



  • pumuckl schrieb:

    wxSkip schrieb:

    @seldon: Was spricht dagegen, std::valarray sowohl als Container als auch als mathematisches Objekt nutzen zu können?

    Es sind zwei völlig unterschiedliche Konzepte. Es bringt nichts, mit dem Trecker auf Rallye fahren zu wollen, genausowenig bringts was, mit dem Rallye-Auto einen pflug zu ziehen. Selbst wenn Rallye-Streifen einen Trecker nicht schwächer machen würden und eine Kupplung für landwirtschaftliche Geräte den Rallye-wagen nicht merklich verlangsamen würden.

    Bloß dass hier das Rallye-Auto den Pflug schneller und mit weniger Sprit ziehen könnte als der Trecker (und teurer wäre es auch nicht).
    Wobei man das das meiste bekommt man wahrscheinlich auch mit std::vector und <algorithm> erreichen kann.



  • Nun ja, zunächst mal, dass das bestehende valarray-Interface sich mit den Anforderungen an einen Container beißt - valarray::resize setzt alle Elemente auf den angegebenen Wert, nicht nur die neuen, und die Konstruktoren passen nicht zusammen, um zwei Beispiele zu nennen.

    Was speziell Iteratoren betrifft, so ist problematisch, dass der Standard es valarray und Konsorten explizit erlaubt, mit Proxy-Objekten um sich zu werfen - jede Funktion, die ein std::valarray<T> zurückgibt, darf stattdessen ein Proxy-Objekt zurückgeben, das aber die selben const-Methoden zur Verfügung stellen muss. Wir befinden uns jetzt also im neuen Standard und schreiben beispielsweise

    std::valarray<int> a;
    
    ...
    
    auto b = -a;
    

    ...und haben ein Proxy-Objekt gecacht, das aus Performancegründen die neuen Werte noch nicht ausgerechnet hat. Jetzt muss es uns möglich sein,

    std::valarray<int>::const_iterator iter = b.begin();
    

    zu schreiben, und wir haben ein Problem: Der Iterator bleibt gültig, bis sich an b etwas entscheidendes ändert. Kommen wir also wie folgt daher:

    std::valarray<int> a(10);
    auto b = -a;
    auto iter = b.begin();
    
    a[0] = 2;
    
    // wie bleibt iter hier gültig?
    

    Übrigens ist

    std::count(&array[0], &array[0]+array.size(), true)
    

    fehlerhafter Code; valarray garantiert nicht, dass die Werte hintereinander im Speicher aufbewahrt werden. Dafür ist etwas wie subarray unproblematisch:

    std::valarray<int> a;
    std::valarray<int> b = a[std::splice(2, 5, 1)]; // 5 Werte ab Element 2 im Abstand von jeweils 1
    


  • pumuckl schrieb:

    Es bringt nichts, mit dem Trecker auf Rallye fahren zu wollen, genausowenig bringts was, mit dem Rallye-Auto einen pflug zu ziehen. Selbst wenn Rallye-Streifen einen Trecker nicht schwächer machen würden und eine Kupplung für landwirtschaftliche Geräte den Rallye-wagen nicht merklich verlangsamen würden.

    Der Vergleich ist mir noch etwas zu nah beieinander. Ich würde es wie folgt ausdrücken: Valarray als Container zu benutzen ist etwa so, als benutze man einen Taschenrechner als Notizblock.



  • seldon schrieb:

    als benutze man einen Taschenrechner als Notizblock.

    Oder das Telefon als Fotoapparat.

    Moment. Ach Mist... 🤡



  • Nur, dass etwas gemacht wurde, bedeutet nicht, dass es eine gute Idee war. :p



  • seldon schrieb:

    Nur, dass etwas gemacht wurde, bedeutet nicht, dass es eine gute Idee war. :p

    Und dass man etwas machen könnte, muss nicht heißen, dass man es auch gleich so machen sollte 😛

    Sprich, wenn du mit deinem Telefon (valarray) fotografieren (iterieren) möchtest (oder umgekehrt), es im Laden (Standard) aber nur "normale" Telefone und Fotoapparate gibt, dann musst du eben den Lötkolben nehmen und deinem Telefon ne Linse oder dem Fotoapparat ne Sprechmuschel einbauen 😉
    Oder jemand war wie du der Meinung, es müsse sowas doch unbedingt geben und verkauft sowas schon fertig (Bibliothek im Netz) 😉



  • seldon schrieb:

    std::valarray<int> a(10);
    auto b = -a;
    auto iter = b.begin();
    
    a[0] = 2;
    
    // wie bleibt iter hier gültig?
    

    Was meinst du mit "gültig"? Speichert a einen Zeiger auf den Proxy und sobald a geändert wird, übernimmt der Proxy die Daten von a, damit die Daten im Proxy nicht verändert werden, aber der Iterator zeigt noch auf a? Wenn ja, warum macht man mit Iterator und Proxy nicht das gleiche wie mit Proxy und valarray?

    Ansonsten verstehe ich das Problem.

    pumuckl schrieb:

    Sprich, wenn du mit deinem Telefon (valarray) fotografieren (iterieren) möchtest (oder umgekehrt), es im Laden (Standard) aber nur "normale" Telefone und Fotoapparate gibt, dann musst du eben den Lötkolben nehmen und deinem Telefon ne Linse oder dem Fotoapparat ne Sprechmuschel einbauen 😉
    Oder jemand war wie du der Meinung, es müsse sowas doch unbedingt geben und verkauft sowas schon fertig (Bibliothek im Netz) 😉

    Dann reiche ich einen Antrag beim Laden ein, weil ich es nicht eilig habe. 😉


Log in to reply