2011-06-17 19 views
10

yo quería comprobar el caso de las siguientes optimizaciones funcionan como se esperaba:Condiciones para la elisión de copia?

  • RVO
  • Named RVO
  • Copia elisión al pasar un argumento por valor

así que escribí este pequeño programa :

#include <algorithm> 
#include <cstddef> 
#include <iostream> 
#include <vector> 

struct Foo { 
    Foo(std::size_t length, char value) : data(length, value) { } 

    Foo(const Foo & rhs) : data(rhs.data) { std::cout << "*** COPY ***" << std::endl; } 

    Foo & operator= (Foo rhs) { 
     std::cout << "*** ASSIGNMENT ***" << std::endl; 
     std::swap(data, rhs.data); // probably expensive, ignore this please 
     return *this; 
    } 

    ~Foo() { } 

    std::vector<char> data; 
}; 

Foo TestRVO() { return Foo(512, 'r'); } 

Foo TestNamedRVO() { Foo result(512, 'n'); return result; } 

void PassByValue(Foo inFoo) {} 

int main() 
{ 
    std::cout << "\nTest RVO: " << std::endl; 
    Foo rvo = TestRVO(); 

    std::cout << "\nTest named RVO: " << std::endl; 
    Foo named_rvo = TestNamedRVO(); 

    std::cout << "\nTest PassByValue: " << std::endl; 
    Foo foo(512, 'a'); 
    PassByValue(foo); 

    std::cout << "\nTest assignment: " << std::endl; 
    Foo f(512, 'f'); 
    Foo g(512, 'g'); 
    f = g; 
} 

Y lo compilé con optimizaciones en abled:

$ g++ -o test -O3 main.cpp ; ./test 

Esta es la salida:

Test RVO: 

Test named RVO: 

Test PassByValue: 
*** COPY *** 

Test assignment: 
*** COPY *** 
*** ASSIGNMENT *** 

De acuerdo con la salida y el trabajo RVO RVO nombrado como era de esperar. Sin embargo, la elisión de copia no se realiza para el operador de asignación y cuando se llama al PassByValue.

¿No se permite copiar elision en los constructores de copia definidos por el usuario? (Sé que RVO está explícitamente permitido por el estándar, pero no sé sobre la elisión de copia al pasar por el valor). ¿Hay alguna manera de verificar la elisión de copia sin definir los constructores de copia?

+1

Para que quede claro, (N) RVO * is * copy elision. No son las únicas formas, pero decir que su ejemplo muestra que la elisión de copia no se realiza es inexacto. –

+0

La elisión de Cooy generalmente está permitida para todos los objetos temporales, pero no para objetos con nombre o vinculados a referencia. Parece que gcc realiza precisamente los permitidos. –

+0

@Dennis Zickefoose gracias, arreglé el texto. – StackedCrooked

Respuesta

9

La forma en que usa el constructor de copia no puede ser eliminada, ya que el objeto copiado aún existe después de la llamada.

Si lo intenta de esta manera, podría funcionar mejor:

PassByValue(Foo(512, 'a')); 

Todas las optimizaciones son permitidos, pero no es necesario, por lo que corresponde a cada compilador de decidir lo que puede y va a hacer.

+0

La optimización se realiza aquí. Este ejemplo junto con la respuesta de @Space_C0wb0y me hacen obtenerlo. – StackedCrooked

10

La norma dice (en el párrafo 08/12/15):

Esta elisión de las operaciones de copia se permitido en las siguientes circunstancias (que puede combinarse para eliminar múltiples copias):

  • en una declaración de devolución en una función con un tipo de devolución de clase , cuando la expresión es el nombre de unno volátilautomática de objetos con el mismo tipo cv-calificada como el tipo de retorno de la función , la operación de copia se puede omite construyendo el automática de objetos directamente en el valor devolución de la función

  • cuando un objeto de clase temporal que no se ha unido a una referencia (12.2) se copia en un objeto clase con el mismo tipo cv-no calificado, la operación de copia se puede omitir por construir el objeto Rary tempo- directamente en el objetivo de la copia omitido

Ninguno de estos casos se aplica aquí, por lo que no se permite que la elisión. El primero es obvio (sin retorno). El segundo no está permitido, porque el objeto que pasa no es temporal.

Tenga en cuenta que su código sigue siendo correcto, ya que tendría que crear la copia de todos modos. Para deshacerse de esa copia, tendrías que usar la semántica de movimiento de C++ 0x.

+1

En C++ 11, el número de sección se convierte en §12.8/31, y hay 2 circunstancias más en las que se permite la elisión copiar/mover, pero están relacionadas solo con el manejo de excepciones. – kennytm

Cuestiones relacionadas