2017-05-25 5 views
0

J'ai fait ce TestClass pour mieux illustrer le problème. Comme vous pouvez voir le constructeur de mouvement est appelé lorsque l'objet est poussé dans le vecteur et lorsque le constructeur est initialisé en utilisant la fonction std::move. Mais quand nous appelons TestClass testvar2(rvalue_func()); vous pouvez voir à la fin, que les valeurs de résultat de la rvalue_func() sont affectés à l'objet testvar2, mais le constructeur de déplacement n'est pas appelé, aussi aucun autre constructeurs ou opérateurs d'affectation ont été appelés ...c + + 11 déplacer constructeur, initialisation par méthode inconnue en utilisant rvalue

La question: Comment a-t-il copié les valeurs de rvalue_func() à testvar2 sans rien appeler et pourquoi le constructeur de déplacement n'a pas été appelé?

#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; 
} 

Résultats:

-------------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 

Répondre

1

C'est une optimisation des compilateurs sont autorisés à effectuer, appelé copy elision (cela semble être le nom même quand il est le constructeur de déplacement qui est éludée). Fondamentalement, le compilateur est parfois autorisé (ou, puisque C++ 17, même requis) à ne pas appeler un constructeur de copie ou de déplacement, si à la place il peut simplement créer l'objet à l'endroit où il sera copié ou déplacé . Dans ce cas, il savait que l'objet entrerait dans testvar2, donc il a simplement créé l'objet là en premier lieu.

optimisations Habituellement compilateur ne sont autorisés à condition qu'un programme conforme n'a aucun moyen de faire la différence entre l'optimisation étant présente et ne pas être présent (par exemple, le remplacement des opérations arithmétiques sur int s avec d'autres qui produisent le même résultat mais sont moins chers pour le CPU à calculer). L'élision de la copie est l'un des rares cas où le compilateur est spécifiquement autorisé à optimiser de manière à pouvoir faire la différence.