unique_ptr in move-assignment operator



  • ScottZhang schrieb:

    Klaus82 schrieb:

    Sehe ich das richtig? Falls ja -> Hä? Warum bleibt es nicht ein rvalue?

    Ja, genau. Weil es "einen Namen hat". Warum weiß ich auch nicht genau, vllt kann das einer der Gurus hier beantworten. Vllt weil man sonst unbemerkt ein wegemovetes objekt übrig haben kann, ka.

    Ja. Es hat einen Namen, also kann man sich mit Hilfe dieses Namens mehrfach auf ein und dasselbe Objekt beziehen. Das hat also gar nichts mehr von einem "temporären Objekt" in dem Kontext. Wenn man da was "moven" will, muss man das eben wieder explizit machen. Das ist gut so; denn man will lieber versehentlich kopieren anstatt versehentlich moven.



  • Aber mal ne ganz andere Frage: Wenn alle Pointer Smartpointer sind, müsste dann nicht sogar der default move-assignment operator funktionieren? Der macht doch auch nichts anderes als elementweise zu moven.



  • TNA schrieb:

    Aber mal ne ganz andere Frage: Wenn alle Pointer Smartpointer sind, müsste dann nicht sogar der default move-assignment operator funktionieren? Der macht doch auch nichts anderes als elementweise zu moven.

    Ja, das würde mich auch interessieren, hatte die Frage ja schon im Eingangspost aufgeworfen. 😃



  • Klaus82 schrieb:

    Ja, das würde mich auch interessieren, hatte die Frage ja schon im Eingangspost aufgeworfen. 😃

    Hab ich doch glatt übersehen 😃
    Schreib doch einfach mal:

    interpolation& interpolation::operator=(interpolation&& rhs) = default;
    

    und schaue ob es kompiliert und das macht, was du möchtest.



  • Also es kompiliert. 🙂

    Was mich nun noch etwas wundert, dass ich dieses Objekt z.B. mittels push_back in einen vector schiebe und der Kompiler sich nicht beschwert.

    Müsste es nicht einen Fehler geben, weil der default Copy Constructor keinen eben solchen für unique_ptr aufrufen kann?

    Gruß,
    -- Klaus.



  • Die meisten std:: Container verlangen keine kopierbaren Elemente mehr. Die müssen aber mindestens Movable sein.



  • Klaus82 schrieb:

    Was mich nun noch etwas wundert, dass ich dieses Objekt z.B. mittels push_back in einen vector schiebe und der Kompiler sich nicht beschwert.

    Wie genau rufst du denn das push_back auf? So dass ein move stattfinden kann?



  • Also ich habe z.B. folgendes Minimalprogramm und kurioser Weise funktioniert dies, obwohl ich dachte, dass wegen der unique_ptr nur die auskommentierte Zeile gehen dürfte.

    Und weil ich einen unique_ptr habe, dachte ich, dass ich Copy Constructor und Assignment Operator verbieten solle, also als private deklarieren sollte.

    //main.cpp
    #include <iostream>
    #include <vector>
    
    #include "interpolation.h"
    
    int main()
    {
    	std::vector<double> x,y;
    
    	x = {0, 1, 2, 3};
    	y = {0, 1, 4, 9};
    
    	std::vector<interpolation> vec;
    
    	//vec.push_back(std::move(interpolation(x,y)));
    	vec.push_back(interpolation(x,y));
    
    	std::cout << vec[0](1.5) << std::endl;
    
    return 0;
    }
    

    Mit folgender header und sourcefile von interpolation

    /*
    interpolation.h
    */
    #ifndef INTERPOLATION_H_
    #define INTERPOLATION_H_
    
    #include <iostream>
    #include <memory>
    #include <vector>
    #include <gsl/gsl_spline.h>
    
    class interpolation
    {
    	public:
    		interpolation();
    		interpolation(std::vector<double>, std::vector<double>);
    		interpolation(interpolation&&) = default;
    		interpolation& operator=(interpolation&&) = default;
    		double operator()(double);
    
    		std::vector<double> x,y;
    		std::unique_ptr<gsl_interp_accel,void(*)(gsl_interp_accel*)> acc;
    		std::unique_ptr<gsl_spline,void(*)(gsl_spline*)> spline;
    
    	private:
    		interpolation(const interpolation&);
    		interpolation& operator=(const interpolation&);
    };
    #endif
    
    /*
    interpolation.cpp
    */
    #include <iostream>
    #include "interpolation.h"
    /*
    ---{}--- default constructor ---{}---
    */
    interpolation::interpolation():
    x(), y(),
    acc(nullptr,nullptr),spline(nullptr,nullptr)
    {}
    /*
    ---{}--- constructor ---{}---
    */
    interpolation::interpolation(std::vector<double> x, std::vector<double> y):
    x(x), y(y),
    acc(gsl_interp_accel_alloc(),gsl_interp_accel_free),
    spline(gsl_spline_alloc(gsl_interp_cspline,x.size()),gsl_spline_free)
    {
    	gsl_spline_init(spline.get(),x.data(),y.data(),x.size());
    }
    /*
    ---{}--- operator() ---{}---
    */
    auto interpolation::operator() (double x) -> double
    {
    	return gsl_spline_eval(spline.get(),x,acc.get());
    }
    

    Gruß,
    -- Klaus.



  • Klaus82 schrieb:

    //main.cpp
    #include <iostream>
    #include <vector>
    
    #include "interpolation.h"
    
    int main()
    {
    	std::vector<double> x,y;
    
    	x = {0, 1, 2, 3};
    	y = {0, 1, 4, 9};
    
    	std::vector<interpolation> vec;
    
    	//vec.push_back(std::move(interpolation(x,y)));
    	vec.push_back(interpolation(x,y));
    
    	std::cout << vec[0](1.5) << std::endl;
    
    return 0;
    }
    

    Das ist klar, dass das funktioniert. Da du das Objekt in dem Funktionsaufruf erzeugst, ist es ein namenloses temporäres Objekt und somit ein rvalue. Es kann also der Move-Konstruktor verwendet werden. Das std::move benötigst du nur, wenn du ein lvalue hast, das aber explizit wie ein rvalue behandelt werden soll. Das ist quasi ein cast. In der Realität würdest du in einem solchen fall natürlich besser gleich emplace_back anstelle vom push_back verwenden.



  • Tipp: Verwende kein std::move wenn es unnötig ist. In den Fällen ist es ggf. sogar kontraproduktiv. Wann gemoved werden kann, weiß der Compiler oft selbst. Die Regeln dazu sind einfach und erfordern keinen besonders intelligenten Compiler. Zum Beispiel ist std::move bei temporären Objekten überflüssig. Es ist auch überflüssig, wenn man ein funktionslokales Objekt per value über retrun zurück geben will. Ggf muss man aus einem copy ein move mit std::move machen; denn alles andere, was einen Namen hat, wird sonst als Lvalue behandelt. Was beim Moven passiert, ist Sache des Klassenautors.


Anmelden zum Antworten