Estou ciente de 5 categorias gerais em que a recompilação de um compilador C ++ 03, pois o C ++ 11 pode causar aumentos ilimitados de desempenho que praticamente não têm relação com a qualidade da implementação. Todas essas são variações da semântica de movimentação.
std::vector
realocar
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 o foo
da memória intermédia é realocada em C ++ 03 que copiados cada vector
em bar
.
No C ++ 11, ele move os bar::data
s, que são basicamente livres.
Nesse caso, isso depende de otimizações dentro do std
contêiner vector
. Em todos os casos abaixo, o uso de std
contêineres é apenas porque eles são objetos C ++ que possuem move
semântica eficiente no C ++ 11 "automaticamente" quando você atualiza seu compilador. Objetos que não o bloqueiam e que contêm um std
contêiner também herdam o aprimoramento automáticomove
construtores .
Falha no NRVO
Quando o NRVO (otimização do valor de retorno nomeado) falha, no C ++ 03 ele volta à cópia, no C ++ 11 volta ao movimento. As falhas do NRVO são fáceis:
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;
}
ou até:
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;
}
Temos três valores - o valor de retorno e dois valores diferentes dentro da função. O Elision permite que os valores dentro da função sejam 'mesclados' com o valor de retorno, mas não entre si. Ambos não podem ser mesclados com o valor de retorno sem mesclar um ao outro.
A questão básica é que a elisão do NRVO é frágil e o código com alterações fora do return
site pode repentinamente ter reduções de desempenho maciças naquele local sem diagnóstico emitido. Na maioria dos casos de falha de NRVO, o C ++ 11 termina com a move
, enquanto o C ++ 03 termina com uma cópia.
Retornando um Argumento de Função
Elision também é impossível aqui:
std::set<int> func(std::set<int> in){
return in;
}
no C ++ 11 isso é barato: no C ++ 03 não há como evitar a cópia. Argumentos para funções não podem ser elididos com o valor retornado, porque a vida útil e a localização do parâmetro e o valor retornado são gerenciados pelo código de chamada.
No entanto, o C ++ 11 pode passar de um para o outro. (Em um exemplo de menos brinquedo, algo pode ser feito para o set
).
push_back
ou insert
Finalmente, a elisão em contêineres não acontece: mas o C ++ 11 sobrecarrega o rvalue move operadores de inserção, o que salva cópias.
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) );
no C ++ 03, um temporário whatever
é criado e, em seguida, copiado para o vetor v
. 2 std::string
buffers são alocados, cada um com dados idênticos, e um é descartado.
No C ++ 11, um temporário whatever
é criado. A whatever&&
push_back
sobrecarga move
é temporária no vetor v
. Um std::string
buffer é alocado e movido para o vetor. Um vazio std::string
é descartado.
Tarefa
Roubado da resposta de @ Jarod42 abaixo.
A elisão não pode ocorrer com a atribuição, mas a mudança pode.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
aqui some_function
retorna um candidato para o qual fugir, mas como ele não é usado para construir um objeto diretamente, ele não pode ser escolhido. No C ++ 03, o acima resulta no conteúdo do temporário sendo copiado some_value
. No C ++ 11, ele é movido para some_value
, o que basicamente é gratuito.
Para o efeito completo do exposto, você precisa de um compilador que sintetize construtores de movimento e atribuição para você.
O MSVC 2013 implementa os construtores de movimentação em std
contêineres, mas não sintetiza os construtores de movimentação nos seus tipos.
Portanto, tipos contendo std::vector
s e similares não obtêm essas melhorias no MSVC2013, mas começarão a obtê-los no MSVC2015.
clang e gcc já implementaram construtores de movimentação implícitos. O compilador de 2013 da Intel oferecerá suporte à geração implícita de construtores de movimentação, se você passar -Qoption,cpp,--gen_move_operations
(eles não fazem isso por padrão em um esforço para serem compatíveis com o MSVC2013).