std::vector befüllen ohne copy constructor?



  • cl90 schrieb:

    Ich hab es jetzt letztendlich auf dem Heap gelöst. also
    vector.pushback(new bla());
    mit entsprechenden deletes an anderen stellen.

    Och nö! Der Ansatz ist sehr fehleranfällig bzgl Speicherlecks. Dann nimm doch wenigstens boost::ptr_vector , wenn du schon keine C++11 Features benutzen willst/kannst.



  • kkaw schrieb:

    cl90 schrieb:

    Ich hab es jetzt letztendlich auf dem Heap gelöst. also
    vector.pushback(new bla());
    mit entsprechenden deletes an anderen stellen.

    Och nö! Der Ansatz ist sehr fehleranfällig bzgl Speicherlecks. Dann nimm doch wenigstens boost::ptr_vector , wenn du schon keine C++11 Features benutzen willst/kannst.

    Ich bin auch nicht zufrieden damit...
    Aber sag mir, kann ich mein Projekt ohne viel aufwand einfach auf C++11 umstellen?
    Ich habe jetzt schon so viele externe Abhängigkeiten und weit mehr als 5000 Zeilen code in über 80 files... Ich weiß nicht wie einfach man das umstellen kann.
    Ich habs mir vorerst nur auf meien ToCheck Liste für das nächste Projekt gesetzt.

    prt_vector regelt das delete selbst?
    Weil das wäre kein Problem. Ich hab Boost x64 bereits im Projekt.



  • Du musst nichts umstellen (ausser vielleicht den Compiler), C++11 ist abwärtskompatibel -- von unbedeutenden Features mal abgesehen.

    Ja, boost::ptr_vector räumt selbst auf. Ich persönlich bin aber spätestens seit C++11 kein Fan mehr von den Pointer-Containern, siehe hier.



  • ok dann werd ich mal lesen wie das geht mit dem umstieg.



  • Nexus schrieb:

    Ja, boost::ptr_vector räumt selbst auf. Ich persönlich bin aber spätestens seit C++11 kein Fan mehr von den Pointer-Containern, siehe hier.

    ...wobei ich im Falle von vector<unique_ptr<T>> die extra Indirektion, die man da durchführen muss, schon nervig finde. Das const wird auch leider nicht propagiert:

    void foo(vector<unique_ptr<int>> const& vec)
    {
      if (!vec.empty()) {
        *vec.front() += 17;  // klappt!
      }
    }
    

    Wenn Du "unhandliche Objekte" nur deswegen einzeln im Heap anlegst und die Adresse in einem unique_ptr speicherst, damit das Speichern im Vektor "effizienter" wird, ist diese Zeiger-Semantik eigentlich nicht das, was man haben will.

    Klar, ich kann mir ausgehend von vector<unique_ptr<T>> immer noch per boost::indirect_iterator zwei Iteratoren bauen, wo ich diese nervige extra Indirektion nicht mehr brauche und worüber auch ein const hinzugefügt werden kann. Das ist schon mal was.

    Aber vielleicht wäre da ein anderer Wrapper nicht ganz verkehrt. Ich habe in meiner Sammlung noch eine Kuh (copy-on-write). Hier nur mal eine vereinfachte und ungetestete Skizze davon:

    template<class T>
    class cow
    {
    public:
        cow()
        {
            static const auto def = make_shared<T>(); // cached default
            ptr = def;
        }
    
        cow(T const& x)
        : ptr(make_shared<T>(x))
        {}
    
        cow(T && x)
        : ptr(make_shared<T>(std::move(x)))
        {}
    
        template<class U, class...More
          ,class=typename std::enable_if<
            !std::is_convertible<U,cow const&>::value &&
            std::is_constructible<T,U,More...>::value
          >::type
        >
        cow(U && x, More && ... xx)
        : ptr(make_shared<T>(forward<U>(x),forward<More>(xx))
        {}
    
        T const& read() const {return *ptr;}
        T const& operator*() const {return *ptr;}
        T const* operator->() const {return ptr.get();}
        operator T const&() const& {return *ptr;}
        operator T const&() const&& = delete; // zu unsicher
    
        T& write()
        {
            if (!ptr.unique()) {
                ptr = make_shared<T>(*ptr);
            }
            assert(ptr.unique());
            return *ptr;
        }
    
    private:
        shared_ptr<T> ptr;
    };
    

    was man dann so nutzen könnte:

    vector<cow<unhandlicher_typ>> vec;
    vec.emplace_back(dies,und,jenes);
    ...
    for (auto& x : vec) {
        x.write().veraendere_dich(1,2,3);
    }
    for (unhandlicher_typ const& x : vec) {
        cout << x.get_something() << endl;
    }
    

    Und sonst könnte man ja auch einfach mal std::deque<T> mit emplace ausprobieren ohne unique_ptr und ohne cow . Da sollte auch nicht so viel kopiert werden. Und das wäre mein aktueller Tipp für cl90: Probier mal std::deque und/oder emplace aus.

    cl90 schrieb:

    ok dann werd ich mal lesen wie das geht mit dem umstieg.

    Ich glaube, beim Microsoft Compiler musst du gar nix besonderes machen. Er unterstützt automatisch schon C++11 Features, sofern einigermaßen aktuell. Beim GCC kommt es auf die Version an. Ggf brauchst du den Compilerschalter "-std=c++0x" oder "-std=c++11".


Anmelden zum Antworten