6

Je lisais Want Speed? Pass by Value sur le C++ Next blog et créé this program pour avoir une idée de copie élision et déplacer la sémantique en C++ 0x:Copier élision sur Visual C++ 2010 Beta 2

#include <vector> 
#include <iostream> 

class MoveableClass { 
public: 
    MoveableClass() : m_simpleData(0), instance(++Instances) { 
     std::cout << "Construct instance " << instance << " (no data)" << std::endl; 
    } 

    MoveableClass(std::vector<double> data) : m_data(std::move(data)), m_simpleData(0), instance(++Instances) { 
     std::cout << "Construct instance " << instance << " (with data)" << std::endl; 
    } 

    MoveableClass(int simpleData) : m_simpleData(simpleData), instance(++Instances) { 
     std::cout << "Construct instance " << instance << " (with simple data)" << std::endl; 
    } 

    MoveableClass(const MoveableClass& other) 
     : m_data(other.m_data), m_simpleData(other.m_simpleData), instance(++Instances) 
    { 
     std::cout << "Construct instance " << instance << " from a copy of " << other.instance << std::endl; 
     Elided = false; 
    } 

    MoveableClass(MoveableClass&& other) 
     : m_data(std::move(other.m_data)), m_simpleData(other.m_simpleData), instance(++Instances) 
    { 
     std::cout << "Construct instance " << instance << " from a move of " << other.instance << std::endl; 
     Elided = false; 
    } 

    MoveableClass& operator=(MoveableClass other) { 
     std::cout << "Assign to instance " << instance << " from " << other.instance << std::endl; 
     other.Swap(*this); 
     return *this; 
    } 

    ~MoveableClass() { 
     std::cout << "Destroy instance " << instance << std::endl; 
     --Instances; 
    } 

    void Swap(MoveableClass& other) { 
     std::swap(m_data, other.m_data); 
     std::swap(m_simpleData, other.m_simpleData); 
    } 

    static int Instances; 
    static bool Elided; 

private: 
    int instance; 
    int m_simpleData; 
    std::vector<double> m_data; 
}; 

int MoveableClass::Instances = 0; 
bool MoveableClass::Elided = true; 

std::vector<double> BunchOfData() { 
    return std::vector<double>(9999999); 
} 

int SimpleData() { 
    return 9999999; 
} 

MoveableClass CreateRVO() { 
    return MoveableClass(BunchOfData()); 
} 

MoveableClass CreateNRVO() { 
    MoveableClass named(BunchOfData()); 
    return named; 
} 

MoveableClass CreateRVO_Simple() { 
    return MoveableClass(SimpleData()); 
} 

MoveableClass CreateNRVO_Simple() { 
    MoveableClass named(SimpleData()); 
    return named; 
} 

int main(int argc, char* argv[]) { 
    std::cout << "\nMove assign from RVO: " << '\n'; 
    { 
     MoveableClass a; 
     a = CreateRVO(); 
    } 
    std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n'; 
    MoveableClass::Elided = true; // reset for next test 

    std::cout << "\nMove assign from RVO simple: " << '\n'; 
    { 
     MoveableClass a; 
     a = CreateRVO_Simple(); 
    } 
    std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n'; 
    MoveableClass::Elided = true; // reset for next test 

    std::cout << "\nMove assign from NRVO: " << '\n'; 
    { 
     MoveableClass a; 
     a = CreateNRVO(); 
    } 
    std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n'; 
    MoveableClass::Elided = true; // reset for next test 

    std::cout << "\nMove assign from NRVO simple: " << std::endl; 
    { 
     MoveableClass a; 
     a = CreateNRVO_Simple(); 
    } 
    std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n'; 
    MoveableClass::Elided = true; // reset for next test 
} 

Voici la sortie que je reçois lors de la compilation en mode de déclenchement sur Visual C++ 10,0 (bêta 2):

mouvement de classer RVO:
Construct exemple 1 (pas de données)
Construct exemple 2 (avec données)
Construct exemple 3 d'un mouvement de 2
Destroy exemple 2
Attribuer à l'instance 1 de 3
Destroy exemple 3
Destroy exemple 1
Déplacer élision: Non

Déplacer assigner de la simple RVO:
Construct exemple 1 (pas de données)
Construct exemple 2 (avec des données simples)
Assigner à une instance de 2
Destro y exemple 2
Détruire exemple 1
mouvement élidée: Oui

mouvement classer de NRVO:
Construct exemple 1 (pas de données)
Construct exemple 2 (avec données)
Assign pour exemple 1 à partir de 2
Détruisez exemple 2
Détruisez exemple 1
Déplacer élision: Oui

Déplacer assigner de la simple NRVO:
Construct exemple 1 (pas de données)
Construct exemple 2 (avec des données simples)
Attribuer à l'instance 1 de 2
Destroy exemple 2
Destroy exemple 1
Déplacer élision: Oui

Cependant , Je suis perplexe par une chose. Comme vous pouvez le voir, tous les mouvements sont élidés sauf le premier. Pourquoi le compilateur ne peut-il pas exécuter RVO avec une MoveableClass (std :: vector) à la ligne 86, mais avec une MoveableClass (int) à la ligne 97? Est-ce juste un bug avec MSVC ou y at-il une bonne raison à cela? Et s'il y a une bonne raison, pourquoi peut-il encore effectuer NRVO sur une MoveableClass (std :: vector) à la ligne 91?

Je voudrais le comprendre afin que je puisse aller dormir heureux. :)

+1

Une très bonne question. Pour ce que ça vaut, g ++ 4.3.3 élide tous ces mouvements, même avec le drapeau '-O0'. – Thomas

+0

Merci Thomas. C'est intéressant que cela fonctionne sur GCC. Peut-être que cela suggère que quelque chose ne va pas avec la mise en œuvre MSVC. – dvide

+1

Je pense que cela souligne à quel point le gouffre est vaste entre "le compilateur devrait" et "le compilateur". – Crashworks

Répondre

1

Hmm.

Il semble que si vous changez le constructeur de données

MoveableClass::MoveableClass(std::vector<double> data) 

accepter le vecteur par référence, comme si,

MoveableClass::MoveableClass(const std::vector<double>& data) 

il fonctionne très bien! Pourquoi ça ne marche pas si vous passez le vecteur par valeur?

Voici également une version qui devrait compiler sur les versions antérieures de MSVC, si quelqu'un veut y faire un test. Il ne contient aucune fonctionnalité C++ 0x: http://pastebin.com/f3bcb6ed1

0

Peut-être que ce serait une bonne idée de mettre à jour et maintenir this example de cpp-next avec une version de votre test qui échoue, donc il peut y avoir un test complet, canonique.

2

Merci de répondre Dave.

J'ai ajouté mes tests à cet exemple:
pastebin.com/f7c8ca0d6

Chose curieuse, il montre que tous les types de élisions ne sont pas effectuées à l'exception NRVO!
Edit: En fait je suppose que c'est parce que c'est le seul test où l'objet a déjà un nom. J'ai aussi essayé d'autres types de LIST et obtenu le même résultat. Cependant, lorsque j'essaie mes propres types sans pod, cela fonctionne comme prévu. Je ne peux pas penser à ce qui est spécial au sujet des types STL qui pourraient causer cela, donc je ne sais pas quoi d'autre à essayer.

Je vais soumettre un rapport de bogue.
Edit: Submitted here

Merci

Questions connexes