std::vector aus Funktion zurückgeben - Pointer?


  • Gesperrt

    Also, wie sollte der Funktionsrückgabetyp sein? (Standard: >=C++11)

    std::vector<T> get_vector1() { ... }
    

    vs.

    std::vector<T> &get_vector1() { ... }
    

    Irgendwie funktioniert beides.(?) Welche Pros und Contras gibt es?

    Use-Case:
    In einer Funktion soll ein vector mit Elementen erstellt werden, wobei der Aufrufer diesen vector weiterverwendet und u. a. verschiedene Funktionen damit aufruft. Der vector enthält std::shared_ptr als Elemente. Der vector soll während des gesamten Programms vorhanden sein.

    Sorry, wegen dieser Baby-Frage...



  • @EinNutzer0

    Was ist { ... }?
    Steht da sowas wie

    std::vector<T> r;
    return r;
    

    Dann funktioniert nicht beides!
    Kurz und knapp:
    In dem Fall oben nimmst du ersters (musst du sogar), wenn der Vector aber z.B. ein member ist und get_vector eine memberfunktion (oder der Vektor aus anderen Gründen den Funktionsaufruf überlebt), dann sowas:

    const std::vector<T> &get_vector1() /*const*/
    

    Hat übrigens nichst mit C++11 zu tun, außer dass man vorher den Vektor ggf als out-Parameter genommen hätte.


  • Gesperrt

    @Jockelx sagte in std::vector aus Funktion zurückgeben - Pointer?:

    Steht da sowas wie
    std::vector<T> r;
    return r;

    Genau, das steht dort...
    Danke für deine Antwort!


  • Mod

    Keines von beiden. Beides inflexibel. Erstes ist ineffizient, wenn man nicht sehr aufpasst. Zweites geht gar nicht, wenn man nicht sehr aufpasst.

    Übliches Pattern: Lass dir einen (templatisierten) Iterator übergeben, in den du schreibst.

    Vorbild: So ziemlich jede Funktion der Standardbibliothek. Besonders häufig im Header algorithms zu beobachten.

    Vorteile:

    • Flexibel
    • Sicher in der Anwendung
    • Klare Trennung von Verantwortungen, und klare Trennung von Aufgaben.

    @EinNutzer0 sagte in std::vector aus Funktion zurückgeben - Pointer?:

    . Der vector soll während des gesamten Programms vorhanden sein.

    Unklare (oder zu weit gefasste) Lebenszeit ist ein Code-Smell. Warum denkst du, das zu brauchen?


  • Gesperrt

    Hier wäre die betreffende Funktion:

    #include <vector>
    #include <unordered_map>
    using namespace std;
    
    unordered_map<uint32_t, vector<shared_ptr<Station1>>> getNeighbours()
    {
        unordered_map<uint32_t, vector<shared_ptr<Station1>>> neighbours;
        for (size_t i = 0; i < systems.size(); i++)
        {
            shared_ptr<System1> sys1 = systems[i];
            for (size_t j = 0; j < systems.size(); j++)
            {
                if (i != j)
                {
                    shared_ptr<System1> sys2 = systems[j];
                    if (sys1->getDis(sys2) <= maxSysDis)
                    {
                        for (auto const &e1 : sys1->stations)
                        {
                            for (auto const &e2 : sys2->stations)
                            {
                                if (neighbours.count(e1->id) <= 0)
                                {
                                    neighbours[e1->id] = vector<shared_ptr<Station1>>();
                                }
                                neighbours[e1->id].push_back(e2);
                            }
                        }
                    }
                }
            }
        }
        return neighbours;
    }
    


  • @SeppJ sagte in std::vector aus Funktion zurückgeben - Pointer?:

    Keines von . Beides inflexibel.

    Geht es noch absoluter!?
    Das ist so absolut jedenfalls quatsch, wie zig andere libs zeigen. Nebenbei wird sogar vector<T> als Standardbeispiel für Rückgabewerte in den Coding guidelines genommen. Wäre das so unsinnig, hätte man das wohl wenigsten dabei geschrieben.



  • @SeppJ sagte in std::vector aus Funktion zurückgeben - Pointer?:

    Keines von beiden. Beides inflexibel. Erstes ist ineffizient, wenn man nicht sehr aufpasst. Zweites geht gar nicht, wenn man nicht sehr aufpasst.
    Übliches Pattern: Lass dir einen (templatisierten) Iterator übergeben, in den du schreibst.

    Das erfordert dann wieder all den Bloat. Ich gebe tatsächlich häufig vector<double> zurück. Ist im Code viel einfacher als mit Iteratoren zu arbeiten. Vergleiche:

    vector<double> getBounds(...some input...);
    ...
    auto bounds = getBounds(...);
    

    mit

    template<typename OutIt>
    void fillBounds(OutIt, ...some input...);
    ...
    vector<double> bounds();
    fillBounds(back_inserter(bounds), ...some input...);
    

    Ersterer Code hat keine Templates (1) und returnt das, was man haben will (2) bzw. was auch wirklich berechnet wird.
    Der untere Code ist generisch (3) und potentiell effizienter (4), wenn man den vector denn wiederverwenden will.

    In meinen Use Cases ist generisch eher ein Nachteil (jetzt muss ich mir auch noch überlegen, was rauskommen soll - und habe Compile-Overhead und bekomme bei Fehlern komplizierte Fehlermeldungen), daher sticht (1) locker (3). Selten ist (4) relevant für mich, aber durch (2) wird der Code einfacher.

    Kommt also schon drauf an, was/wozu man was macht. Ich übergebe auch in 99.9% ganze vectoren an Funktion (d.h. begin und end als "ganzer vector" mitgezählt), und wenn man statt der Iteratoren die vector-constref übergibt, verliert man zwar Generalität, gewinnt aber Einfachheit. Gut, und "C++ Style" ist es dann auch nicht mehr 😉


  • Mod

    @Jockelx sagte in std::vector aus Funktion zurückgeben - Pointer?:

    Geht es noch absoluter!?
    Das ist so absolut jedenfalls quatsch, wie zig andere libs zeigen. Nebenbei wird sogar vector<T> als Standardbeispiel für Rückgabewerte in den Coding guidelines genommen. Wäre das so unsinnig, hätte man das wohl wenigsten dabei geschrieben.

    Die Coding-Guidelines sagen im selben Tipp, auf den du dich wahrscheinlich beziehst, dass Ownership-Transfer selten ist und üblicherweise vermieden werden soll (eben indem man die Ownership durch Übergabe von Zeigern/Iteratoren klar definiert). Sie sagen auch, dass man sich selber gute Libraries schreiben solle, wenn es keine guten gibt, und dass die Standardlibrary als Vorbild dienen kann, was gut ist.



  • @SeppJ sagte in std::vector aus Funktion zurückgeben - Pointer?:

    Die Coding-Guidelines sagen im selben Tipp, auf den du dich wahrscheinlich beziehst

    Ich beziehe mich -überraschender Weise- auf "Parameter passing F.15". Und wo findet bei einer const-ref Rückgabe bitte Ownership-Transfer statt?


  • Mod

    @Jockelx sagte in std::vector aus Funktion zurückgeben - Pointer?:

    @SeppJ sagte in std::vector aus Funktion zurückgeben - Pointer?:

    Die Coding-Guidelines sagen im selben Tipp, auf den du dich wahrscheinlich beziehst

    Ich beziehe mich -überraschender Weise- auf "Parameter passing F.15". Und wo findet bei einer const-ref Rückgabe bitte Ownership-Transfer statt?

    Nicht in dem Tipp, denn ich dachte du beziehst dich auf den zur Rückgabe, da es hier schließlich um Rückgaben geht.

    Bezüglich der Eingabe: Und? Wenn man weiter liest, kommt man auch mal bei G(eneric programming) an und sieht, dass man sich nicht ohne Not einschränken sollte. Es ist doch schon sehr selten, dass man einen Fall hat, wo vector<X> genau der Typ ist, den man braucht. Entweder braucht nur man eine Sequence von X (oft sogar etwas noch abstrakteres) oder man hat eine Klasse mit einem vector-artigem Member (z.B. wie string so etwas wie ein vector<char> + Funktionalität) ist. Und zu 99% ist X nicht einmal fest sondern kann ein Template sein. Wenn man da möglichst unabhängig bleibt, ist man allgemeiner als auch potentiell schneller.

    Was halt alles in den späteren Kapiteln steht.

    (Genauer ist ein String ja auch kein vector<char> sondern ein template<typename char_type> vector<char_type>…)