6

stavo leggendo Want Speed? Pass by Value sulla C++ Next blog e creato this program per ottenere un tatto per la copia elision e spostare la semantica in C++ 0x:Copia elision su 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 
} 

Ecco l'uscita ottengo quando compilato in modalità di rilascio su Visual C++ 10.0 (Beta 2):

movimento assegnare da RVO:
Construct esempio 1 (senza dati)
Construct esempio 2 (con dati)
Construct esempio 3 da una mossa di 2
Destroy esempio 2
Assegnare all'istanza 1 da 3
Destroy esempio 3
Distruggi esempio 1
Sposta eliso: No

Spostare assegnare da RVO semplice:
Costruire esempio 1 (senza dati)
Construct esempio 2 (con dati semplici)
Assign per esempio 1 da 2
Destro y esempio 2
distruggere esempio 1
movimento elisa: Si

movimento assegnare da NRVO:
Construct esempio 1 (senza dati)
Construct esempio 2 (con dati)
Assign per esempio 1 da 2
Distruggi esempio 2
Distruggi esempio 1
Sposta eliso: Sì

Spostare assegnare da NRVO semplice:
Construct esempio 1 (nessun dato)
Construct esempio 2 (con dati semplici)
Assegnare all'istanza 1 da 2
Destroy esempio 2
Distruggi esempio 1
Sposta eliso: Sì

Tuttavia , Sono perplesso di una cosa. Come puoi vedere, tutte le mosse sono eluite tranne il primo. Perché il compilatore non può eseguire RVO con un MoveableClass (std :: vector) alla riga 86, ma può con un MoveableClass (int) alla riga 97? Questo è solo un bug con MSVC o c'è una buona ragione per questo? E se c'è una buona ragione, perché può ancora eseguire NRVO su un MoveableClass (std :: vector) alla riga 91?

Mi piacerebbe capirlo così posso andare a dormire felice. :)

+1

Una domanda molto buona. Per quel che vale, g ++ 4.3.3 elide tutte queste mosse, anche con il flag '-O0'. – Thomas

+0

Grazie Thomas. È interessante che funzioni su GCC. Forse questo suggerisce qualcosa di sbagliato nell'implementazione di MSVC. – dvide

+1

Penso che questo sottolinei la vastità della voragine tra "il compilatore dovrebbe" e "il compilatore". – Crashworks

risposta

1

Hmm.

Sembra che se si cambia il costruttore di dati

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

per accettare il vettore con riferimento, in questo modo,

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

funziona benissimo! Perché non funziona se si passa il vettore in base al valore?

Inoltre, ecco una versione che dovrebbe essere compilata nelle versioni precedenti di MSVC, se qualcuno vuole eseguire il test lì. Non contiene funzioni C++ 0x: http://pastebin.com/f3bcb6ed1

0

Forse sarebbe una buona idea aggiornare e mantenere this example da cpp-next con una versione del test che non riesce, quindi ci può essere un test canonico completo.

2

Grazie per aver risposto Dave.

ho aggiunto il mio test a quell'esempio:
pastebin.com/f7c8ca0d6

Curiosamente dimostra che tutti i tipi di elisioni non vengono eseguiti ad eccezione di NRVO!
Edit: In realtà suppongo che questo è perché è l'unico test in cui l'oggetto abbia mai un nome.

Ho anche provato altri tipi di STL e ho ottenuto lo stesso risultato. Tuttavia, quando provo i miei tipi non pod, funziona come previsto. Non riesco a pensare che cosa c'è di speciale nei tipi di STL che potrebbero causare questo, quindi non so che altro provare.

Presenterò una segnalazione di errore.
Edit: Submitted here

Grazie

Problemi correlati