Ir para o conteúdo

Última atualização:2026-10-12

Princípio e uso de std::move em C++

std::move é uma função muito importante: ela fornece uma forma de marcar um objeto lvalue como um tipo especial de objeto rvalue, o xvalue. Essa transformação existe para permitir a execução da semântica de movimento, em vez da semântica de cópia convencional.

  • Por que usar std::move
  • Cenários reais de aplicação

1.Por que usar std::move

Normalmente, apenas objetos rvalue podem sofrer operações de movimento, porque o construtor de movimento usa uma referência rvalue como parâmetro, e referências rvalue só podem ser vinculadas a rvalues. Portanto, se você quiser executar uma operação de movimento a partir de um objeto representado por uma expressão lvalue, precisará usar std::move para convertê-lo em um xvalue (um rvalue especial).

Use std::move com cuidado, porque a operação de movimento deixa o objeto em um estado que normalmente só permite a destruição. Por isso, o ideal é realizar a operação de movimento apenas no último uso do objeto.

std::move não executa o movimento de fato; é preciso deixar claro que std::move não realiza a operação de movimento do objeto. Ele apenas converte o objeto lvalue dado em uma referência rvalue. Abaixo está uma implementação possível de std::move:

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
    return static_cast<typename remove_reference<T>::type&&>(t);
}

2.Cenários reais de uso

2.1 Construção por movimento em vez de construção por cópia

Na função f1, o parâmetro a é um lvalue do tipo referência rvalue, e não um rvalue em si, porque tem um nome. Portanto, para forçar a construção por movimento de Foo, devemos convertê-lo em um rvalue, e std::move faz exatamente isso de forma perfeita.

struct Foo { 
    ... 
    Foo(const Foo&) {} 
    Foo(Foo&&) {} 
}; 

void f1(Foo&& a) { 
    Foo b(a); // 调用拷贝构造函数,而非移动构造函数
} 

void f2(Foo&& a) { 
    Foo b(std::move(a)); // right
} 

2.2 Gestão de propriedade (ownership)

Suponha o seguinte código:

Foo* p = new Foo();
data.push_back(p);

O código acima funciona normalmente, mas não é possível determinar a quem pertence a propriedade do ponteiro. Quem deve deletar o ponteiro? Ou quando o ponteiro deve ser deletado? Ele será deletado quando o contêiner for destruído?

No C++11, a instrução std::unique_ptr<Foo> p(new Foo()); gera um erro de compilação. A razão é que a regra de propriedade única é aplicada.

Porém, você pode usar std::move para transferir a propriedade do ponteiro:

data.push_back(std::move(p));

Agora, a propriedade do ponteiro foi transferida para o contêiner, e ele será deletado no fim do tempo de vida do contêiner. Embora o objeto p ainda exista, qualquer tentativa de desreferenciá-lo gerará uma exceção de ponteiro nulo.

Na verdade, após a operação de movimento, o ponteiro interno que p possuía já foi definido como nullptr.

Em resumo, o papel de std::move é fornecer um meio de implementar com segurança a semântica de movimento de objetos, transformando sua natureza de lvalue em rvalue, para que eles possam ser usados pelo construtor de movimento ou pelo operador de atribuição por movimento. Usar std::move corretamente pode melhorar o desempenho e garantir a transferência correta de propriedade.

Então, a próxima pergunta: quando não é necessário usar std::move?

Tópicos relacionados

Comentários