O que é std :: move () e quando deve ser usado?

  1. O que é isso?
  2. O que isso faz?
  3. Quando deve ser usado?

Bons links são apreciados.

http://en.wikipedia.org/wiki/C%2B%2B11#Rvalue_references_and_move_constructors
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Move_Semantics

  1. Em C ++ 11, além de copiar construtores, os objects podem ter construtores de movimento.
    (Além de copiar os operadores de atribuição, eles têm operadores de atribuição de movimentação.)
  2. O construtor de movimento é usado em vez do construtor de cópia, se o object tiver o tipo “rvalue-reference” ( Type && ).
  3. std::move() é uma conversão que produz uma referência de valor para um object, para permitir a movimentação a partir dele.

É uma nova maneira C ++ para evitar cópias. Por exemplo, usando um construtor de movimento, um std::vector poderia simplesmente copiar seu ponteiro interno para dados para o novo object, deixando o object movido em um estado incorreto, evitando copiar todos os dados. Isso seria C ++ – válido.

Tente googling para mover semântica, rvalue, encaminhamento perfeito.

Você pode usar mover quando precisar “transferir” o conteúdo de um object para outro lugar, sem fazer uma cópia (por exemplo, o conteúdo não é duplicado, por isso pode ser usado em alguns objects não copiáveis, como um unique_ptr). Também é possível para um object obter o conteúdo de um object temporário sem fazer uma cópia (e economizar muito tempo), com std :: move.

Esse link realmente me ajudou:

http://thbecker.net/articles/rvalue_references/section_01.html

Me desculpe se minha resposta está chegando tarde demais, mas eu também estava procurando por um bom link para o std :: move, e achei os links acima um pouco “austeros”.

Isso coloca a ênfase na referência do valor-r, em que contexto você deve usá-los, e eu acho que é mais detalhado, é por isso que eu queria compartilhar este link aqui.

1. “O que é isso?”

Enquanto std::move() “se parece com” uma function – eu diria que não é realmente uma function . É uma espécie de conversor entre as formas como o compilador considera o valor de uma expressão.

2. “O que isso faz?”

A primeira coisa a notar é que std::move() na verdade não move nada .

Se você já assistiu a série de animação Bleach – ela faz o equivalente ao suavizante Reishi de Quincy Seele Schneider .

Sério, no entanto, ele converte uma expressão de um lvalue ou puro rvalue (como uma variável que você pode estar usando por um longo tempo ainda, ou um temporário que você está passando por um tempo, respectivamente) para ser um xvalue . Um xvalue diz ao compilador:

Você pode me pilhar, mover qualquer coisa que eu esteja segurando e usá-lo em outro lugar (já que eu vou ser destruído logo de qualquer maneira) “.

Em outras palavras, quando você usa std::move(x) , você está permitindo que o compilador canibalize x . Assim, se x tem, digamos, seu próprio buffer na memory – depois de std::move() o compilador pode ter outro object em seu lugar.

3. “Quando deve ser usado?”

Outra maneira de fazer essa pergunta é “O que eu utilizaria / canibalizaria os resources de um object?” bem, se você está escrevendo código de aplicação, você provavelmente não estaria mexendo muito com objects temporários criados pelo compilador. Então, principalmente, você faria isso em locais como construtores, methods de operador, funções semelhantes a algoritmos STL, etc., onde objects são criados e destruídos de forma automagica. Claro, isso é apenas uma regra de ouro.

Um uso típico é ‘mover’ resources de um object para outro em vez de copiar. @Guillaume links para esta página, que tem um exemplo simples: trocar dois objects com menos cópias.

 template  swap(T& a, T& b) { T tmp(a); // we now have two copies of a a = b; // we now have two copies of b (+ discarded a copy of a) b = tmp; // we now have two copies of tmp (+ discarded a copy of b) } 

O uso da movimentação permite trocar os resources em vez de copiá-los:

 template  swap(T& a, T& b) { T tmp(std::move(a)); a = std::move(b); b = std::move(tmp); } 

Pense no que acontece quando T é, digamos, vector de tamanho n. Na primeira versão você lê e escreve 3 * n elementos, na segunda versão você basicamente lê e escreve apenas os 3 pointers para os buffers dos vetores. É claro que a class T precisa saber como fazer o movimento; você deve ter um operador de atribuição de movimento e um construtor de movimento para a class T para que isso funcione.

std :: move em si não faz muito. Eu pensei que ele chamou o construtor movido para um object, mas ele realmente apenas executa um tipo de conversão (convertendo uma variável lvalue em um rvalue para que a dita variável possa ser passada como um argumento para um construtor de movimento ou operador de atribuição).

Então std :: move é usado apenas como um precursor para usar a semântica de movimento. Mover a semântica é essencialmente uma maneira eficiente de lidar com objects temporários.

Considere o object A = B + C + D + E + F;

Este é um código bonito, mas o E + F produz um object temporário. Então D + temp produz outro object temporário e assim por diante. Em cada operador “+” normal de uma class, ocorrem cópias profundas.

Por exemplo

 Object Object::operator+ (const Object& rhs) { Object temp (*this); // logic for adding return temp; } 

A criação do object temporário nessa function é inútil – esses objects temporários serão excluídos no final da linha, assim que saírem do escopo.

Podemos usar a semântica de movimento para “saquear” os objects temporários e fazer algo como

  Object& Object::operator+ (Object&& rhs) { // logic to modify rhs directly return rhs; } 

Isso evita que cópias profundas desnecessárias sejam feitas. Com referência ao exemplo, a única parte em que ocorre a cópia profunda é agora E + F. O resto usa a semântica de movimento. O construtor de movimento ou o operador de atribuição também precisa ser implementado para atribuir o resultado a A.

Q: O que é std::move

A: std::move() é uma function da Biblioteca Padrão C ++ para conversão em uma referência de valor.

Simplisticamente std::move(t) é equivalente a:

 static_cast(t); 

Um rvalue é um temporário que não persiste além da expressão que o define, como um resultado de function intermediária que nunca é armazenado em uma variável.

 int a = 3; // 3 is a rvalue, does not exist after expression is evaluated int b = a; // a is a lvalue, keeps existing after expression is evaluated 

Uma implementação para std :: move () é dada em N2027: “Uma Breve Introdução às Referências de Valor” da seguinte forma:

 template  typename remove_reference::type&& std::move(T&& a) { return a; } 

Como você pode ver, std::move retorna T&& não importa se chamado com um valor ( T ), tipo de referência ( T& ) ou referência de valor ( T&& ).

Q: O que isso faz?

A: Como um casting, ele não faz nada durante o tempo de execução. É relevante apenas em tempo de compilation dizer ao compilador que você gostaria de continuar considerando a referência como um rvalue.

 foo(3 * 5); // obviously, you are calling foo with a temporary (rvalue) int a = 3 * 5; foo(a); // how to tell the compiler to treat `a` as an rvalue? foo(std::move(a)); // will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)` 

O que não faz:

  • Faça uma cópia do argumento
  • Chame o construtor de cópia
  • Alterar o object do argumento

P: Quando deve ser usado?

R: Você deve usar o std::move se quiser chamar funções que suportem mover a semântica com um argumento que não seja um rvalue (expressão temporária).

Isso implora as seguintes perguntas de acompanhamento para mim:

  • O que são as semânticas de movimento? Mover a semântica em contraste com a semântica de cópia é uma técnica de programação na qual os membros de um object são inicializados por “assumir” em vez de copiar os membros de outro object. Tal “assumir” faz sentido apenas com pointers e manipuladores de resources, que podem ser transferidos de forma barata copiando o ponteiro ou o identificador inteiro em vez dos dados subjacentes.

  • Que tipo de classs e objects suportam mover semântica? Cabe a você, como desenvolvedor, implementar a semântica de movimento em suas próprias classs, se elas se beneficiarem da transferência de seus membros, em vez de copiá-las. Depois de implementar a semântica de movimento, você se beneficiará diretamente do trabalho de muitos programadores de biblioteca que adicionaram suporte para manipular classs com a movimentação da semântica de maneira eficiente.

  • Por que o compilador não consegue descobrir sozinho? O compilador não pode simplesmente chamar outra sobrecarga de uma function, a menos que você o diga. Você deve ajudar o compilador a escolher se a versão regular ou de movimento da function deve ser chamada.

  • Em quais situações eu gostaria de dizer ao compilador que ele deveria tratar uma variável como um rvalue? Isso provavelmente acontecerá nas funções de modelo ou biblioteca, onde você sabe que um resultado intermediário pode ser recuperado.

What is it? e o What does it do? foi explicado acima.

Vou dar um exemplo de when it should be used.

Por exemplo, temos uma class com muitos resources, como grande matriz.

 class ResHeavy{ // ResHeavy means heavy resource public: ResHeavy(int len=10):_upInt(new int[len]),_len(len){ cout< <"default ctor"< _upInt; // heavy array resource int _len; // length of int array }; 

Código de teste:

 void test_std_move2(){ ResHeavy rh; // only one int[] // operator rh // after some operator of rh, it becomes no-use // transform it to other object ResHeavy rh2 = std::move(rh); // rh becomes invalid // show rh, rh2 it valid if(rh.is_up_valid()) cout< <"rh valid"< 

saída como abaixo:

 default ctor move ctor rh invalid rh2 valid copy ctor rh3 valid 

Podemos ver que o move constructor std::move with move constructor torna o recurso de transformação mais fácil.