2017-05-25 4 views
0

Ich habe diese TestClass gemacht, um das Problem besser zu veranschaulichen. Wie Sie sehen können, wird der Verschiebungskonstruktor aufgerufen, wenn das Objekt in den Vektor geschoben wird und wenn der Konstruktor mit der Funktion std::move initialisiert wird. Aber wenn wir nennen TestClass testvar2(rvalue_func()); Sie am Ende sehen können, dass die Ergebniswerte des rvalue_func() zu testvar2 Objekt zugeordnet sind, aber der Umzug Konstruktor wird nicht genannt, auch keine anderen Konstrukteuren oder Zuweisungsoperatoren genannt wurden ...C++ 11 Move Konstruktor, Initialisierung durch unbekannte Methode mit rvalue

Die Frage: Wie hat es die Werte von rvalue_func() zu testvar2 kopiert, ohne irgendetwas aufzurufen und warum der Move-Konstruktor nicht aufgerufen wurde?

#include <iostream> 
using std::cout; 
using std::endl; 

#include <vector> 

class TestClass { 
public: 

    TestClass(int arg_x=0, int arg_y=0) : 
      values { nullptr } { 

     values = new int[2]; 
     values[0] = arg_x; 
     values[1] = arg_y; 
     cout << "default constructor " << "x = " << values[0] << " y = " 
       << values[1] << endl; 
    } 

    TestClass(const TestClass &arg) : 
      values { nullptr } { 

     values = new int[2]; 

     values[0] = arg.values[0]; 
     values[1] = arg.values[1]; 

     cout << "copy constructor " << "x = " << values[0] << " y = " 
       << values[1] << endl; 
    } 

    TestClass(TestClass &&arg) : 
      values { arg.values } { 
     arg.values = nullptr; 
     cout << "move constructor " << "x = " << values[0] << " y = " 
       << values[1] << endl; 
    } 

    TestClass &operator=(TestClass &right) { 
     cout << "assignment operator =" << endl; 
     if (this != &right) { 
      delete values; 
      values = nullptr; 
      values = new int[2]; 
      values[0] = right.values[0]; 
      values[1] = right.values[2]; 
     } 
     return *this; 
    } 

    TestClass &operator=(TestClass &&right) { 
     cout << "move assignment operator =" << endl; 
     if (this != &right) { 
      delete values; 
      values = right.values; 
      right.values = nullptr; 
     } 
     return *this; 
    } 

    void print() { 
     if (values != nullptr) 
      cout << "x = " << values[0] << " y = " << values[1] << endl; 
    } 
private: 
    int *values; 
}; 

TestClass rvalue_func() { 
    cout << "creating TestClass temp" << endl; 
    TestClass temp(100, 200); 
    cout << "TestClass temp is created" << endl; 
    return temp; 
} 
void test_rvalues() { 
    cout << "-------------vector push back--------------" << endl; 
    std::vector<TestClass> test_vector; 
    test_vector.push_back(TestClass(1, 2)); 

    cout << "-----rvalue constructor with std::move-----" << endl; 
    TestClass testvar1(std::move(rvalue_func())); 

    cout << "------------rvalue constructor-------------" << endl; 
    TestClass testvar2(rvalue_func()); 


    cout << "-------------------------------------------" << endl; 
    cout << "testvar2 values "; 
    testvar2.print(); 
} 

int main(int argc, char *argv[]) { 
    test_rvalues(); 

    return 0; 
} 

Ergebnisse:

-------------vector push back-------------- 
default constructor x = 1 y = 2 
move constructor x = 1 y = 2 
-----rvalue constructor with std::move----- 
creating TestClass temp 
default constructor x = 100 y = 200 
TestClass temp is created 
move constructor x = 100 y = 200 
------------rvalue constructor------------- 
creating TestClass temp 
default constructor x = 100 y = 200 
TestClass temp is created 
------------------------------------------- 
testvar2 values x = 100 y = 200 

Antwort

1

Dies ist eine Optimierung, die Compiler ausführen dürfen, die so genannte copy elision (dies scheint auch der Name zu sein, wenn es der Umzug Konstruktor ist die elided ist). Der Compiler ist manchmal erlaubt (oder, seit C++ 17, sogar erforderlich), einen Copy- oder Move-Konstruktor nicht aufzurufen, wenn er stattdessen das Objekt nur an der Stelle erzeugen kann, an die es kopiert oder verschoben werden soll . In diesem Fall wusste es, dass das Objekt in testvar2 gehen würde, so dass es das Objekt dort an erster Stelle erstellt hat. Normalerweise sind Compiler-Optimierungen nur zulässig, solange ein entsprechendes Programm keine Möglichkeit hat, den Unterschied zwischen vorhandener und nicht vorhandener Optimierung zu erkennen (z. B. Ersetzen von arithmetischen Operationen auf int s durch andere, die dasselbe erzeugen) Ergebnis sind aber billiger für die CPU zu berechnen). Copy elision ist einer der wenigen Fälle, in denen der Compiler speziell so optimiert werden kann, dass Sie den Unterschied erkennen können.

Verwandte Themen