Il move semantics è un meccanismo di C++ che consente di trasferire efficientemente risorse tra oggetti, anziché copiarle. Con l'introduzione di C++11 sono stati introdotti i costruttori e gli operatori di assegnazione di spostamento, che permettono agli oggetti di trasferire le proprie risorse a un altro oggetto senza costi di copia.
std::move trasforma l'oggetto passato in un riferimento rvalue, segnalando che le sue risorse possono essere trasferite in modo sicuro. std::forward viene utilizzato nei template per mantenere il tipo di valore (lvalue/rvalue) quando viene passato ulteriormente.
#include <string> #include <vector> #include <iostream> std::vector<std::string> getNames() { std::vector<std::string> v = {"Alice", "Bob", "Charlie"}; return v; // Qui si attiva il move semantics } int main() { std::vector<std::string> names = getNames(); // Qui i contenuti di getNames() verranno trasferiti senza copia superflua }
Qual è la differenza tra std::move e un costruttore di spostamento? Se uso std::move, l'oggetto verrà sempre trasferito?
Risposta:
std::move è semplicemente un cast a riferimento rvalue, non esegue di per sé il trasferimento. L'operazione di spostamento avverrà solo se esiste veramente un costruttore/operator di spostamento implementato. In caso contrario, avverrà una copia.
struct A { A() = default; // nessun costruttore di spostamento A(const A&) { std::cout << "Copio! "; } }; A a1; A a2 = std::move(a1); // Viene chiamata la copia, non il trasferimento
Storia
-In uno dei sistemi finanziari, è stata ottimizzata la gestione delle stringhe sostituendo le copie con std::move, ma la struttura non aveva costruttori di spostamento implementati. La copia è rimasta, le prestazioni non sono aumentate, è emersa una falsa certezza di accelerazione del codice.
Storia
-Un sviluppatore ha eseguito std::move() su una variabile che ha continuato a usare dopo il trasferimento. I dati si sono trovati in uno stato incoerente, l'applicazione è caduta periodicamente.
Storia
-Nel codice di libreria server, gli oggetti temporanei venivano accettati tramite riferimento const, e poi si cercava di usare std::move(), aspettandosi un trasferimento. Il risultato - copia, operazioni inefficaci e aumento della latenza sotto carichi elevati.