Sono a conoscenza di 5 categorie generali in cui la ricompilazione di un compilatore C ++ 03 come C ++ 11 può causare aumenti delle prestazioni illimitati che sono praticamente estranei alla qualità dell'implementazione. Queste sono tutte variazioni della semantica del movimento.
std::vector
riallocare
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
ogni volta che il foo
buffer viene riallocato in C ++ 03 viene copiato ogni vector
in bar
.
In C ++ 11 sposta invece la bar::data
s, che è sostanzialmente gratuita.
In questo caso, questo si basa su ottimizzazioni all'interno del std
contenitore vector
. In ogni caso di seguito, l'uso dei std
container è dovuto al fatto che sono oggetti C ++ che hanno una move
semantica efficiente in C ++ 11 "automaticamente" quando si aggiorna il compilatore. Gli oggetti che non lo bloccano che contengono un std
contenitore ereditano anche i move
costruttori automatici migliorati .
Fallimento NRVO
Quando NRVO (denominato ottimizzazione del valore restituito) fallisce, in C ++ 03 ricade sulla copia, su C ++ 11 ricade sulla mossa. I guasti di NRVO sono facili:
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 anche:
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;
}
Abbiamo tre valori: il valore restituito e due diversi valori all'interno della funzione. Elision consente di "unire" i valori all'interno della funzione con il valore restituito, ma non tra loro. Entrambi non possono essere uniti al valore di ritorno senza unirsi tra loro.
Il problema di base è che l'elissione della NRVO è fragile e che il codice con modifiche non vicine al return
sito può improvvisamente avere massicce riduzioni delle prestazioni in quel punto senza emettere diagnosi. Nella maggior parte dei casi di fallimento NRVO, C ++ 11 finisce con a move
, mentre C ++ 03 finisce con una copia.
Restituzione di un argomento di funzione
Anche qui Elision è impossibile:
std::set<int> func(std::set<int> in){
return in;
}
in C ++ 11 è economico: in C ++ 03 non c'è modo di evitare la copia. Gli argomenti alle funzioni non possono essere elisi con il valore restituito, poiché la durata e la posizione del parametro e del valore restituito sono gestite dal codice chiamante.
Tuttavia, C ++ 11 può spostarsi dall'uno all'altro. (In un esempio meno giocattolo, si potrebbe fare qualcosa per set
).
push_back
o insert
Infine, l'elisione nei container non avviene: ma C ++ 11 sovraccarica il rvalue move inserendo operatori, che salva le copie.
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) );
in C ++ 03 whatever
viene creato un temporaneo , quindi viene copiato nel vettore v
. std::string
Vengono allocati 2 buffer, ognuno con dati identici e uno viene scartato.
In C ++ 11 whatever
viene creato un temporaneo . Il whatever&&
push_back
sovraccarico è quindi move
temporaneo nel vettore v
. Un std::string
buffer viene allocato e spostato nel vettore. Un vuoto std::string
viene scartato.
Incarico
Rubato dalla risposta di @ Jarod42 qui sotto.
L'elisione non può avvenire con l'assegnazione, ma può spostarsi da.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
qui some_function
restituisce un candidato da cui fuggire, ma poiché non viene utilizzato per costruire direttamente un oggetto, non può essere eluso. In C ++ 03, quanto sopra si traduce nella copia del contenuto del temporaneo some_value
. In C ++ 11, viene spostato in some_value
, che in pratica è gratuito.
Per l'effetto completo di quanto sopra, è necessario un compilatore che sintetizzi i costruttori di movimenti e l'assegnazione per te.
MSVC 2013 implementa i costruttori di spostamento in std
contenitori, ma non sintetizza i costruttori di spostamento sui tipi.
Quindi i tipi che contengono se std::vector
simili non ottengono tali miglioramenti in MSVC2013, ma inizieranno a ottenerli in MSVC2015.
clang e gcc hanno da tempo implementato costruttori di mosse implicite. Il compilatore Intel 2013 supporterà la generazione implicita di costruttori di spostamenti se si passa -Qoption,cpp,--gen_move_operations
(non lo fanno per impostazione predefinita nel tentativo di essere cross-compatibili con MSVC2013).