O que posso fazer com um objeto movido de?

138

O padrão define precisamente o que posso fazer com um objeto depois que ele foi movido? Eu costumava pensar que tudo o que você pode fazer com um objeto movido é destruí-lo, mas isso não seria suficiente.

Por exemplo, considere o modelo de função swapconforme definido na biblioteca padrão:

template <typename T>
void swap(T& a, T& b)
{
    T c = std::move(a); // line 1
    a = std::move(b);   // line 2: assignment to moved-from object!
    b = std::move(c);   // line 3: assignment to moved-from object!
}

Obviamente, deve ser possível atribuir a objetos movidos, caso contrário as linhas 2 e 3 falharão. Então, o que mais posso fazer com objetos movidos de? Onde exatamente posso encontrar esses detalhes no padrão?

(A propósito, por que está em T c = std::move(a);vez da T c(std::move(a));linha 1?)

fredoverflow
fonte

Respostas:

53

Os objetos removidos existem em um estado não especificado, mas válido. Isso sugere que, embora o objeto possa não ser mais capaz de fazer muito, todas as suas funções de membro ainda devem exibir um comportamento definido - incluindo operator=- e todos os seus membros em um estado definido - e ainda requer destruição. O Padrão não fornece definições específicas porque seria exclusivo para cada UDT, mas você pode encontrar especificações para os tipos Padrão. Alguns contêineres semelhantes são relativamente óbvios - eles apenas movem seu conteúdo e um contêiner vazio é um estado válido bem definido. As primitivas não modificam o objeto movido de.

Nota lateral: Eu acredito que é T c = std::move(a)para que, se o construtor de movimentação (ou construtor de cópia, se nenhuma movimentação for fornecida) for explícito, a função falhará.

Cachorro
fonte
26
Nem todas as suas funções de membro exibirão um comportamento definido. Somente aqueles sem pré-condições. Por exemplo, você provavelmente não deseja pop_backmudar de vector. Mas você certamente pode descobrir se é empty().
Howard Hinnant
6
@ Howard Hinnant: pop_backde um vazio vectortem um comportamento indefinido de qualquer maneira, da memória, por isso tenho certeza de que a pop_backpartir de um vetor movido que exibe um comportamento indefinido é consistente.
Filhote de cachorro
12
Estamos discutindo objetos movidos. Não são objetos conhecidos como estando em um estado vazio. Os objetos removidos têm um estado não especificado (a menos que seja especificado de outra forma). [lib.types.movedfrom]
Howard Hinnant
5
@ Howard Não especificado, mas válido, portanto pop_backainda se comporta como em qualquer vetor válido (pode até ser um vetor vazio).
Christian Rau
1
o que significam não especificado e válido neste contexto?
precisa
114

17.6.5.15 [lib.types.movedfrom]

Objetos dos tipos definidos na biblioteca padrão C ++ podem ser movidos de (12.8). As operações de movimentação podem ser especificadas explicitamente ou geradas implicitamente. Salvo especificação em contrário, esses objetos movidos devem ser colocados em um estado válido, mas não especificado.

Quando um objeto está em um estado não especificado, você pode executar qualquer operação no objeto que não tenha condições prévias. Se houver uma operação com pré-condições que você deseja executar, não será possível executá-la diretamente porque você não sabe se o estado não especificado do objeto satisfaz as pré-condições.

Exemplos de operações que geralmente não têm pré-condições:

  • destruição
  • tarefa
  • observadores const, como get, empty,size

Exemplos de operações que geralmente têm pré-condições:

  • desreferência
  • pop_back

Esta resposta agora aparece no formato de vídeo aqui: http://www.youtube.com/watch?v=vLinb2fgkHk&t=47m10s

Howard Hinnant
fonte
1
Mas eu poderia simplesmente verificar as condições prévias como qualquer outro objeto, certo?
fredoverflow
6
@FredOverflow Desde que essas verificações não possuam pré-condições, é claro.
Christian Rau
1
@ Chris: Mas como isso é diferente de um objeto normal, e não movido de?
fredoverflow
2
Pode ser que seja uma pergunta à parte, mas isso significa: se eu tiver uma string com char* buffer;e int length;membros, meu construtor / designação de movimento deve trocar (ou definir) o valor de ambos? Ou seria bom se o comprimento não fosse especificado (significando isso emptye sizeretornando valores sem sentido)?
UncleBens
3
@ 6502: Você não faz sentido. Uma classe C ++ 03 não está "violando o padrão C ++ 0x" porque um controlador de movimento, se gerado , violaria o padrão. Como o código C ++ 03 não estaria movendo essa classe, não há motivo para gerar um movedor.
MSalters 12/08/19