6

estaba leyendo Want Speed? Pass by Value en el C++ Next blog y creé this program para conseguir una sensación para la copia elisión y mover la semántica en C++ 0x:Copia elisión en 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 
} 

Aquí está la salida consigo cuando se compila en modo de lanzamiento en Visual C++ 10.0 (Beta 2):

Move asignar de RVO:
Construct ejemplo 1 (sin datos)
Construct ejemplo 2 (con datos)
Construct ejemplo 3 a partir de un movimiento de 2
Destruye ejemplo 2
Asignar a ejemplo 1 a partir de 3
Destruye ejemplo 3
Destruye ejemplo 1
Move elidido: No

Move asignar de RVO simple:
Construir ejemplo 1 (sin datos)
Construct ejemplo 2 (con datos simples)
Asignar a ejemplo 1 a partir de 2
Destro y ejemplo 2
Destruye ejemplo 1
Move elidido: Sí

Move asignar de NRVO:
Construct ejemplo 1 (sin datos)
Construct ejemplo 2 (con datos)
Asignar a ejemplo 1 a partir de 2
Destruye ejemplo 2
Destruye ejemplo 1
Mover elidido: Sí

Mover asignar de NRVO simple:
Construct ejemplo 1 (sin datos)
Construct ejemplo 2 (con datos simples)
Asignar a ejemplo 1 a partir de 2
Destruye ejemplo 2
Destruye ejemplo 1
Move elidido: Sí

Sin embargo , Estoy perplejo por una cosa. Como puede ver, todos los movimientos se eliminan a excepción de la primera. ¿Por qué no puede el compilador realizar RVO con un MoveableClass (std :: vector) en la línea 86, pero puede hacerlo con un MoveableClass (int) en la línea 97? ¿Es solo un error con MSVC o hay una buena razón para esto? Y si hay una buena razón, ¿por qué todavía puede realizar NRVO en un MoveableClass (std :: vector) en la línea 91?

Me gustaría entenderlo para poder dormir feliz. :)

+1

una muy buena pregunta. Por lo que vale, g ++ 4.3.3 elide todos estos movimientos, incluso con el indicador '-O0'. – Thomas

+0

Gracias Thomas. Es interesante que funciona en GCC. Tal vez eso sugiere que algo está mal con la implementación de MSVC. – dvide

+1

Creo que esto subraya cuán vasto es el abismo entre "el compilador debería" y "el compilador sí". – Crashworks

Respuesta

1

Hmm.

Parece que si cambia el constructor de datos

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

para aceptar el vector de referencia, como tal,

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

funciona bien! ¿Por qué no funciona si pasas el vector por valor?

También aquí hay una versión que debería compilarse en versiones anteriores de MSVC, si alguien quiere ejecutar la prueba allí. No contiene características de C++ 0x: http://pastebin.com/f3bcb6ed1

0

Quizás sería una buena idea actualizar y mantener this example desde cpp-next con una versión de prueba que falla, por lo que puede haber una prueba integral y canónica.

2

Gracias por responder Dave.

He añadido mis pruebas a ese ejemplo:
pastebin.com/f7c8ca0d6

Curiosamente muestra que todos los tipos de elisiones no se llevan a cabo a excepción de NRVO!
Editar: En realidad, supongo que esto se debe a que es la única prueba donde el objeto tiene un nombre.

También probé otros tipos de STL y obtuve el mismo resultado. Sin embargo, al probar mis propios tipos que no son pod, funciona como se esperaba. No puedo pensar qué tiene de especial los tipos de STL que podrían estar causando esto, así que no sé qué más probar.

Voy a enviar un informe de error.
Editar: Submitted here

Gracias

Cuestiones relacionadas