Conozco 5 categorías generales en las que recompilar un compilador de C ++ 03 ya que C ++ 11 puede causar aumentos de rendimiento ilimitados que prácticamente no están relacionados con la calidad de la implementación. Estas son todas las variaciones de la semántica de movimiento.
std::vector
redistribuir
struct bar{
std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03
cada vez que el foo
buffer 's se reasigna en C ++ 03 se copian todos los vector
en bar
.
En C ++ 11, en cambio, mueve el bar::data
s, que es básicamente libre.
En este caso, esto se basa en optimizaciones dentro del std
contenedor vector
. En todos los casos a continuación, el uso de std
contenedores es solo porque son objetos C ++ que tienen una move
semántica eficiente en C ++ 11 "automáticamente" cuando actualiza su compilador. Los objetos que no lo bloquean y que contienen un std
contenedor también heredan los move
constructores mejorados automáticos .
Fallo de NRVO
Cuando NRVO (optimización del valor de retorno denominado) falla, en C ++ 03 vuelve a copiarse, en C ++ 11 vuelve a caer en movimiento. Las fallas de NRVO son fáciles:
std::vector<int> foo(int count){
std::vector<int> v; // oops
if (count<=0) return std::vector<int>();
v.reserve(count);
for(int i=0;i<count;++i)
v.push_back(i);
return v;
}
o incluso:
std::vector<int> foo(bool which) {
std::vector<int> a, b;
// do work, filling a and b, using the other for calculations
if (which)
return a;
else
return b;
}
Tenemos tres valores: el valor de retorno y dos valores diferentes dentro de la función. Elision permite que los valores dentro de la función se 'fusionen' con el valor de retorno, pero no entre sí. Ambos no pueden fusionarse con el valor de retorno sin fusionarse entre sí.
El problema básico es que la elisión de NRVO es frágil, y el código con cambios que no están cerca del return
sitio de repente puede tener reducciones masivas de rendimiento en ese punto sin emitir diagnóstico. En la mayoría de los casos de falla NRVO, C ++ 11 termina con a move
, mientras que C ++ 03 termina con una copia.
Devolver un argumento de función
Elision también es imposible aquí:
std::set<int> func(std::set<int> in){
return in;
}
en C ++ 11 esto es barato: en C ++ 03 no hay forma de evitar la copia. Los argumentos de las funciones no se pueden elidir con el valor de retorno, ya que el código de llamada administra la vida útil y la ubicación del parámetro y el valor de retorno.
Sin embargo, C ++ 11 puede moverse de uno a otro. (En un ejemplo de menos juguete, se podría hacer algo al set
).
push_back
o insert
Finalmente, la elisión en contenedores no ocurre: pero C ++ 11 sobrecarga los operadores de inserción de movimiento de valor, lo que guarda copias.
struct whatever {
std::string data;
int count;
whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );
en C ++ 03 whatever
se crea un temporal , luego se copia en el vector v
. std::string
Se asignan 2 buffers, cada uno con datos idénticos, y uno se descarta.
En C ++ 11 whatever
se crea un temporal . La whatever&&
push_back
sobrecarga entonces move
es temporal en el vector v
. Se std::string
asigna un búfer y se traslada al vector. Se std::string
descarta un vacío .
Asignación
Robado de la respuesta de @ Jarod42 a continuación.
Elision no puede ocurrir con la asignación, pero se puede mover desde.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
aquí some_function
devuelve un candidato para eludir, pero debido a que no se utiliza para construir un objeto directamente, no se puede elidir. En C ++ 03, lo anterior hace que se copie el contenido de lo temporal some_value
. En C ++ 11, se traslada a some_value
, que básicamente es gratuito.
Para obtener el efecto completo de lo anterior, necesita un compilador que sintetice los constructores de movimiento y la asignación por usted.
MSVC 2013 implementa constructores de movimiento en std
contenedores, pero no sintetiza constructores de movimiento en sus tipos.
Por lo tanto, los tipos que contienen std::vector
sy similares no obtienen tales mejoras en MSVC2013, pero comenzarán a obtenerlas en MSVC2015.
clang y gcc han implementado desde hace mucho tiempo constructores de movimientos implícitos. El compilador 2013 de Intel admitirá la generación implícita de constructores de movimientos si aprueba -Qoption,cpp,--gen_move_operations
(no lo hacen de forma predeterminada en un esfuerzo por ser compatibles con MSVC2013).