Um move constructor da classe T
é um constructor sem modelo cujo primeiro parâmetro é T&&, const T&&, volatile T&&, ou const volatile T&&, e ou não há outros parâmetros, ou o resto dos parâmetros todos têm valores padrão.
>
Conteúdo
- 1 Sintaxe
- 2 Explicação
- 3 Construtor de movimento implícito
- 4 Construtor de movimento implícito
- 5 Construtor de movimento trivial
- 6 Construtor de movimento elegível
- 7 Implicitamente-construtor de movimento definido
- 8 Notas
- 9 Exemplo
- 10 Relatórios de defeitos
- 11 Veja também
Sintaxe
nome_da_classe ( class_name && ) |
(1) | (desde C++11) | |||||||
nome_da_classe ( class_name && ) = padrão; |
(2) | (desde C++11) | |||||||
class_name ( class_name && ) = apagar; |
(3) | (desde C++11) | |||||||
Onde class_name deve nomear a classe atual (ou a instanciação atual de um template de classe), ou, quando declarado no namespace scope ou em uma declaração de amigo, deve ser um nome de classe qualificado.
Explicação
O construtor move é normalmente chamado quando um objeto é inicializado (por inicialização direta ou por cópia-inicialização) a partir de rvalue (xvalue ou prvalue) (até C++17)xvalue (desde C+++17) do mesmo tipo, incluindo
- inicialização: T a = std::move(b); ou T a(std::move(b));, onde
b
é do tipoT
; - argumento da função passando: f(std::move(a)));, onde
a
é do tipoT
ef
é nulo f(T t); - function return: return a; dentro de uma função como T f(), onde
a
é do tipoT
que tem um construtor de movimento.
Quando o inicializador é um prvalue, a chamada construtor de movimento é frequentemente optimizada para fora (até C++17)nunca feita (desde C++17), veja copy elision.
Mover construtores tipicamente “roubam” os recursos detidos pelo argumento (e.g. apontadores para objetos alocados dinamicamente, descritores de arquivos, soquetes TCP, fluxos I/O, threads em execução, etc.) ao invés de fazer cópias deles, e deixar o argumento em algum estado válido, mas indeterminado. Por exemplo, mover-se de um std::string ou de um std::vector pode resultar em deixar o argumento vazio. No entanto, este comportamento não deve ser confiado. Para alguns tipos, tais como std::unique_ptr, o estado movido de um estado é totalmente especificado.
Construtor de movimento implícito
Se nenhum construtor de movimento definido pelo usuário for fornecido para um tipo de classe (struct, class, ou union), e tudo o que se segue é verdade:
- não há construtores de cópia declarados pelo usuário;
- não há operadores de atribuição de cópia declarados pelo usuário;
- não há operadores de atribuição de movimento declarados pelo usuário;
- não há destrutor declarado pelo usuário.
então o compilador irá declarar um construtor de mudanças como não explícito inline public
membro da sua classe com a assinatura T::T(T&&)
.
Uma classe pode ter construtores move múltiplos, por exemplo, tanto T::T(const T&&) como T::T(T&&). Se alguns construtores de movimento definidos pelo usuário estiverem presentes, o usuário ainda pode forçar a geração do construtor de movimento implicitamente declarado com a palavra-chave default
.
O construtor de movimento implicitamente declarado (ou declarado por padrão em sua primeira declaração) tem uma especificação de exceção como descrita na especificação de exceção dinâmica (até C++17)especificação de exceção (desde C++17)
Construtor de movimento implicitamente declarado excluído
O construtor de movimento implicitamente declarado ou declarado por padrão para a classe T
é definido como excluído se alguma das seguintes opções for verdadeira:
-
T
possui membros de dados não estáticos que não podem ser movidos (possui construtores de movimentos excluídos, inacessíveis ou ambíguos); -
T
possui uma classe base direta ou virtual que não pode ser movida (possui construtores de movimentos excluídos, inacessíveis ou ambíguos); -
T
tem uma classe base direta ou virtual com um destruidor excluído ou inacessível; -
T
é uma classe tipo união e tem um membro variante com construtor de mudanças não trivial.
Um construtor de movimento padrão que é excluído é ignorado por resolução de sobrecarga (caso contrário, isso impediria a inicialização da cópia de valor).
Construtor de movimento trivial
O construtor de movimento para a classe T
é trivial se tudo o que se segue for verdade:
- não é fornecido pelo usuário (ou seja, é implicitamente definido ou padrão);
-
T
não tem funções de membro virtual; -
T
não tem classes base virtuais; - o construtor move selecionado para cada base direta de
T
é trivial; - o construtor move selecionado para cada tipo de classe não-estática (ou array do tipo de classe) membro de
T
é trivial.
Um construtor move trivial é um construtor que executa a mesma ação que o construtor copy trivial, ou seja, faz uma cópia da representação do objeto como se fosse por std::memmove. Todos os tipos de dados compatíveis com a linguagem C (tipos POD) são trivialmente móveis.
Construtor de movimento elegível
Um construtor de movimento é elegível se não for excluído. |
(até C++20) |
Um construtor de mudanças é elegível se
|
(desde C+++20) |
A trivialidade dos construtores move elegíveis determina se a classe é um tipo de vida implícita, e se a classe é um tipo trivialmente copiável.
Construtor de movimento implícito
Se o construtor de movimento implícito não for excluído ou trivial, ele é definido (ou seja, um corpo de função é gerado e compilado) pelo compilador se odr-usado ou necessário para avaliação constante. Para tipos de união, o construtor de movimentos implicitamente definido copia a representação do objeto (como por std::memmove). Para tipos de classes não-unionais (classe e estrutura), o construtor de movimentações executa movimentações de membros completos das bases do objeto e membros não-estáticos, em sua ordem de inicialização, usando inicialização direta com um argumento xvalue. Se isto satisfaz os requisitos de um construtor constexpr, o construtor move gerado é constexpr
.
Notas
Para tornar possível a garantia de exceção forte, construtores move definidos pelo usuário não devem lançar exceções. Por exemplo, std::vector depende de std::move_if_noexcepção para escolher entre mover e copiar quando os elementos precisam ser realocados.
Se ambos os construtores copy e move são fornecidos e nenhum outro construtor é viável, a resolução de sobrecarga seleciona o construtor move se o argumento for um valor do mesmo tipo (um valor x como o resultado de std::move ou um prvalue como um temporário sem nome (até C++17)), e seleciona o construtor copy se o argumento for um valor l (objeto nomeado ou uma função/operador retornando referência lvalue). Se apenas o construtor de cópia é fornecido, todas as categorias de argumento o selecionam (desde que leve uma referência para const, uma vez que os valores podem ser vinculados a referências const), o que faz com que a cópia de segurança para mover, quando mover não esteja disponível.
Um construtor é chamado de ‘move constructor’ quando leva uma referência rvalue como parâmetro. Não é obrigatório mover nada, a classe não é obrigada a ter um recurso a ser movido e um ‘construtor move’ pode não ser capaz de mover um recurso como no caso permitido (mas talvez não sensato) onde o parâmetro é uma referência de valor constante (const T&&).
Exemplo
#include <string>#include <iostream>#include <iomanip>#include <utility> struct A{ std::string s; int k; A() : s("test"), k(-1) { } A(const A& o) : s(o.s), k(o.k) { std::cout << "move failed!\n"; } A(A&& o) noexcept : s(std::move(o.s)), // explicit move of a member of class type k(std::exchange(o.k, 0)) // explicit move of a member of non-class type { }}; A f(A a){ return a;} struct B : A{ std::string s2; int n; // implicit move constructor B::(B&&) // calls A's move constructor // calls s2's move constructor // and makes a bitwise copy of n}; struct C : B{ ~C() { } // destructor prevents implicit move constructor C::(C&&)}; struct D : B{ D() { } ~D() { } // destructor would prevent implicit move constructor D::(D&&) D(D&&) = default; // forces a move constructor anyway}; int main(){ std::cout << "Trying to move A\n"; A a1 = f(A()); // return by value move-constructs the target from the function parameter std::cout << "Before move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; A a2 = std::move(a1); // move-constructs from xvalue std::cout << "After move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; std::cout << "Trying to move B\n"; B b1; std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "\n"; B b2 = std::move(b1); // calls implicit move constructor std::cout << "After move, b1.s = " << std::quoted(b1.s) << "\n"; std::cout << "Trying to move C\n"; C c1; C c2 = std::move(c1); // calls copy constructor std::cout << "Trying to move D\n"; D d1; D d2 = std::move(d1);}
>
Output:
Trying to move ABefore move, a1.s = "test" a1.k = -1After move, a1.s = "" a1.k = 0Trying to move BBefore move, b1.s = "test"After move, b1.s = ""Trying to move Cmove failed!Trying to move D
Relatórios de defeitos
Os seguintes relatórios de defeitos com mudança de comportamento foram aplicados retroativamente aos padrões C++ publicados anteriormente.
DR | Aplicado a | Comportamento como publicado | Comportamento correto |
---|---|---|---|
CWG 1402 | C+++11 | um construtor de movimento padrão que chamaria um construtor de cópia não trivial foi excluído; um construtor de movimento padrão que é excluído continua participando da resolução de sobrecarga |
permite a chamada a esse construtor de cópia; sempre ignorado na resolução de sobrecarga |
CWG 2094> | C++11 | um subobjecto volátil feito de um padrão mova construtor nãotrivial (CWG496) |
trivialidade não afectada |
Ver também
- construtor de conversão
- atribuição de cópia
- construtor de cópia
- elisão de cópia
- construtor padrão
- destrutor
- explícito
- inicialização
- inicialização agregada
- inicialização constante
- inicialização de cópia
- inicialização padrão
- inicialização direta
- lista de inicializadores
- inicialização de lista
- inicialização de referência
- inicialização de valor
- inicialização de valor
- inicialização zero
- atribuição de movimento
- novo