Verwirrung mit move-Constructor und std::thread



  • Servus,

    #include <iostream>
    #include <string>
    #include <thread>
    
    class A
    {
    public:
    	A()
    	{
    
    	}
    
    	A(A const &rhs)
    	{
    		std::cout << "A(A const &rhs)\n";
    	}
    
    	A(A&& rhs)
    	{
    		std::cout << "A(A&& rhs)\n";
    	}
    };
    
    void foo(A a)
    {
    	std::cout << "foo()\n";
    }
    
    int main()
    {
    	A a;
    
    	std::thread t(&foo, std::move(a));
    	t.join();
    
    	return 0;
    }
    

    Ausgabe:

    A(A&& rhs)
    A(A&& rhs)
    A(A&& rhs)
    A(A&& rhs)
    A(A const &rhs)
    foo()
    
    1. Wieso wird der move-Konstruktor 4mal aufgerufen?
    2. Wieso wird der copy-Konstruktor aufgerufen? Wie kann ich das verhindern?

    Schonmal danke ..



  • @2) Der simpelste Weg, das verhindern wäre ihn zu deleten (oder halt zu verstecken/nicht implementieren)



  • Wenn ich das mache bekomme ich einen Fehler vom Compiler. Was aber eigentlich auch logisch ist, da foo() den Paramater by-value nimmt und nicht der dazugehörige Copy-constructor gefunden werden kann, oder?

    Dann verstehe ich aber nicht mehr den Sinn von der move-Semantik, und erst recht nicht wieso der Move-constructor 4mal vorher aufgerufen wird ...



  • std::forward, std::ref und std::cref sind deine Freunde.
    Edit: Doch nicht.



  • A(A&& rhs)
    A(A&& rhs)
    A(A&& rhs)
    A(A&& rhs)
    A(A const &rhs)
    foo()
    

    😕

    Dein Compiler unterstützt C++11 noch nicht ganz. Den Kopierkonstruktor hier aufzurufen ist gar nicht erlaubt, der Typ könnte ja Move-Only sein.

    Der GCC gibt hier aus:

    A(A&& rhs)
    A(A&& rhs)
    A(A&& rhs)
    foo()
    


  • Und der dreimalige Aufruf des Move-Konstruktors liegen an der GCC-Implementierung von std::thread.



  • Nathan schrieb:

    Und der dreimalige Aufruf des Move-Konstruktors liegen an der GCC-Implementierung von std::thread.

    Wieso es genau drei sind verstehe ich nicht.

    Eigentlich brauchts doch nur 2: 1x move um A in std::thread zu speichern und 1x um Foo aufzurufen.



  • Ok, das würde es erklären.
    Ich benutze Visual Studio 2013 Professional, ich dachte der Compiler würde das mittlerweile unterstützen? 😕



  • htasaed schrieb:

    Nathan schrieb:

    Und der dreimalige Aufruf des Move-Konstruktors liegen an der GCC-Implementierung von std::thread.

    Wieso es genau drei sind verstehe ich nicht.

    Eigentlich brauchts doch nur 2: 1x move um A in std::thread zu speichern und 1x um Foo aufzurufen.

    Soweit ich jetzt auf die Schnelle den Sourecode verstehe:
    Der Konstruktor ruft std::__bind_simple mit den Argumenten auf.
    std::__bind_simple erzeugt ein Objekt was vom Typ std::_Bind_simple ist.
    std::_Bind_simple speichert die Argumente und den Funktionspointer in einem tuple, daher wohl der zusätzliche move.


Log in to reply