Condições para geração automática de operador padrão / copiar / mover e copiar / mover atribuição?

127

Quero atualizar minha memória nas condições em que um compilador normalmente gera automaticamente um construtor padrão, um construtor de cópias e um operador de atribuição.

Lembro-me de que havia algumas regras, mas não me lembro e também não consigo encontrar um recurso respeitável online. Alguém pode ajudar?

oompahloompah
fonte

Respostas:

136

A seguir, "gerado automaticamente" significa "declarado implicitamente como padrão, mas não definido como excluído". Há situações em que as funções-membro especiais são declaradas, mas definidas como excluídas.

  • O construtor padrão é gerado automaticamente se não houver construtor declarado pelo usuário (§12.1 / 5).
  • O construtor de cópia é gerado automaticamente se não houver construtor de movimentação declarado pelo usuário ou operador de atribuição de movimentação (porque não há construtores de movimentação ou operadores de atribuição de movimentação no C ++ 03, isso simplifica para "sempre" no C ++ 03) ( §12.8 / 8).
  • O operador de atribuição de cópia é gerado automaticamente se não houver construtor de movimentação declarado pelo usuário ou operador de atribuição de movimentação (§12.8 / 19).
  • O destruidor é gerado automaticamente se não houver destruidor declarado pelo usuário (§12.4 / 4).

Apenas C ++ 11 e posterior:

  • O construtor de movimentação é gerado automaticamente se não houver construtor de cópia declarado pelo usuário, operador de atribuição de cópia ou destruidor e se o construtor de movimentação gerado for válido (§12.8 / 10).
  • O operador de atribuição de movimentação é gerado automaticamente se não houver construtor de cópia declarado pelo usuário, operador de atribuição de cópia ou destruidor e se o operador de atribuição de movimentação gerado for válido (por exemplo, se não for necessário atribuir membros constantes) (§12.8 / 21)
Philipp
fonte
9
Um destruidor herdado conta? Quero dizer, digamos que tenho uma classe base com um destruidor virtual vazio. Isso impede a criação de construtores de movimentação nas subclasses? Se a resposta for sim, ajudará se eu definir um construtor de movimento na classe base?
Kamilk
10
Eu acho que você deve mencionar talvez que ter constmembros na classe irá impedir o construtor de ser auto-gerado ...
nonsensickle
"Há situações em que as funções especiais de membro são declaradas, mas definidas como excluídas." consulte onde você tem, por exemplo, membros const ou de referência para onde a mudança será impossível? Não, não pode ser, porque a cópia será aplicada.
towi
Eu sei que é restrito o envio de hiperlinks neste fórum. Mas também é um bom artigo - cplusplus.com/articles/y8hv0pDG
bruziuz 12/12/16
Observe que, como padrão, um construtor de cópias implicitamente padronizado " é preterido se a classe tiver um operador de atribuição de cópia declarada pelo usuário ou um destruidor declarado pelo usuário " ( 12.8 Copiando e movendo objetos de classe [class.copy] ).
29517 sigy
98

Achei o diagrama abaixo muito útil.

Regras C ++ para construtores automáticos e operadores de atribuição de Sticky Bits - Tornando-se uma regra do herói zero

Marco M.
fonte
Lindo. A que se refere "independente"? Independente do que?
towi
8
O copiador / tarefa é 'independente' um do outro. Se você escrever apenas um, o compilador fornecerá o outro. Por outro lado, se você fornecer um controlador de movimentação ou uma atribuição de movimentação, o compilador não fornecerá o outro.
Marco M.
Gostaria de saber qual é o motivo por trás das operações de cópia serem independentes. Razões históricas podem ser? ou o fato de que a cópia não modifica seu destino, mas a mudança muda?
RaGa__M 5/17/17
@Explorer_N Sim, compatibilidade com versões anteriores, motivos históricos. Foi uma escolha ruim de design há muito tempo, então agora há uma necessidade de boas práticas como a "regra dos três" (defina todas as três ou nenhuma: construtor de cópias, operador de atribuição de cópias e muitas vezes destruidor) para evitar erros difíceis de encontrar.
atablash
@MarcoM., Até onde eu entendi, a condição "Se você escreve ..." inclui os dois casos de definir a função de membro especial como = delete(óbvio) ou = default(menos óbvio para mim). Estou certo?
Enrico Maria De Angelis
2

Rascunho padrão C ++ 17 N4659

Para uma referência rápida entre padrões, consulte as seções "Declaradas implicitamente" das seguintes entradas de preferência:

Naturalmente, a mesma informação pode ser obtida no padrão. Por exemplo, em rascunho padrão C ++ 17 N4659 :

15.8.1 "Copiar / mover construtores" diz para o construtor de cópias:

6 Se a definição de classe não declarar explicitamente um construtor de cópia, um não-explícito será declarado implicitamente. Se a definição de classe declara um construtor de movimentação ou um operador de atribuição de movimentação, o construtor de cópia declarado implicitamente é definido como excluído; caso contrário, é definido como padrão (11.4). O último caso será preterido se a classe tiver um operador de atribuição de cópia declarada pelo usuário ou um destruidor declarado pelo usuário.

e para mover construtor:

8 Se a definição de uma classe X não declarar explicitamente um construtor de movimento, um não explícito será implicitamente declarado como padrão se e somente se

  • (8.1) - X não possui um construtor de cópias declarado pelo usuário,

  • (8.2) - X não possui um operador de atribuição de cópia declarado pelo usuário,

  • (8.3) - X não possui um operador de atribuição de movimentação declarado pelo usuário e

  • (8.4) - X não possui um destruidor declarado pelo usuário.

15.8.2 "Copiar / mover operador de atribuição" diz para atribuição de cópia:

2 Se a definição de classe não declarar explicitamente um operador de atribuição de cópia, um será declarado implicitamente. Se a definição de classe declara um construtor de movimentação ou um operador de atribuição de movimentação, o operador de atribuição de cópia declarado implicitamente é definido como excluído; caso contrário, é definido como padrão (11.4). O último caso será descontinuado se a classe tiver um construtor de cópias declarado pelo usuário ou um destruidor declarado pelo usuário.

e para atribuição de movimento:

4 Se a definição de uma classe X não declarar explicitamente um operador de atribuição de movimentação, um será implicitamente declarado como padrão se e somente se

  • (4.1) - X não possui um construtor de cópias declarado pelo usuário,
  • (4.2) - X não possui um construtor de movimentação declarado pelo usuário,
  • (4.3) - X não possui um operador de atribuição de cópia declarado pelo usuário e
  • (4.4) - X não possui um destruidor declarado pelo usuário.

15.4 "Destruidores" diz isso para destruidores:

4 Se uma classe não tiver um destruidor declarado pelo usuário, um destruidor é declarado implicitamente como padrão (11.4). Um destruidor implicitamente declarado é um membro público interno de sua classe.

Ciro Santilli adicionou uma nova foto
fonte