A semântica de movimento em C++ permite transferir recursos de objetos temporários diretamente para outros objetos, evitando operações de cópia profunda. Isso é crucial para a eficiência, especialmente em código genérico, onde o encaminhamento perfeito de parâmetros é essencial.
Valores à esquerda e à direita
Valores à esquerda referem-se a locais de memória que podem ser endereçados, enquanto valores à direita representam dados temporários. Antes do C++11, referências a valores à direita eram limitadas a referências constantes. A introdução de referências de valor à direita (T&&) no C++11 suporta a semântica de movimento.
Semântica de Movimento em Classes
Classes podem definir construtores de cópia e operadores de atribuição com semântica de movimento para transferir recursos de forma eficiente. Por exemplo:
class Recurso {
public:
Recurso(Recurso&& outro) noexcept {
// Transferir recursos de 'outro' para este objeto
}
Recurso& operator=(Recurso&& outro) noexcept {
if (this != &outro) {
// Liberar recursos existentes e transferir de 'outro'
}
return *this;
}
};
Função std::move
Para converter valores à esquerda em referências de valor à direita, utilize std::move. Isso é útil em operações como troca de valores:
template<typename T>
void troca(T& a, T& b) {
T temporario = std::move(a);
a = std::move(b);
b = std::move(temporario);
}
Quando T é uma classe com muitos recursos, isso melhora significativamente a performance.
Encaminhamento Perfeito de Parâmetros
Em templates, o encaminhamento de parâmetros deve preservar suas características sem sobrecarga. No C++11, referências de valor à direita permitem encaminhamento perfeito. O desafio é que, em templates, parâmetros de valor à direita podem se tornar valores à esquerda. Considere:
void processa(int v) {}
template<typename T>
void wrapper(T a) {
processa(a); // Isso pode não encaminhar corretamente
}
Para encaminhamento perfeito, use referências de valor à direita e a função std::forward:
void processa(int& v) {}
void processa(int&& v) {}
template<typename T>
void wrapper(T&& arg) {
processa(std::forward<T>(arg));
}
int main() {
int x = 5;
wrapper(x); // Encaminha como valor à esquerda
wrapper(10); // Encaminha como valor à direita
}
Regras de Colapso de Referências
No C++11, regras de colapso de referências determinam como múltiplos & em tipos de template são resolvidos. Por exemplo:
T& &torna-seT&T& &&torna-seT&T&& &torna-seT&T&& &&torna-seT&&
Essas regras garantem que o encaminhamento de parâmetros mantenha a corretude dos tipos.
Função std::forward
std::forward é usada para encaminhar parâmetros preservando sua categoria de valor. Em templates com T&&, a dedução de tipo converte valores à direita em valores à esquerda, mas std::forward restaura a categoria original:
#include <iostream>
void teste(int& x) { std::cout << "valor à esquerda" << std::endl; }
void teste(int&& x) { std::cout << "valor à direita" << std::endl; }
template<typename T>
void encaminha(T&& arg) {
teste(std::forward<T>(arg));
}
int main() {
int a = 1;
encaminha(a); // Saída: valor à esquerda
encaminha(std::move(a)); // Saída: valor à direita
encaminha(42); // Saída: valor à direita
}
O encaminhamento perfeito resolve problemas de eficiência com objetos temporários e é amplamente usado em bibliotecas como STL em funções como std::make_pair e std::make_unique.