Originalmente, eu postei isso como uma pergunta apenas sobre destruidores, mas agora estou adicionando consideração ao construtor padrão. Aqui está a pergunta original:
Se eu quiser dar à minha classe um destruidor que seja virtual, mas que seja igual ao que o compilador geraria, eu posso usar
=default
:class Widget { public: virtual ~Widget() = default; };
Mas parece que posso obter o mesmo efeito com menos digitação usando uma definição vazia:
class Widget { public: virtual ~Widget() {} };
Existe alguma maneira pela qual essas duas definições se comportam de maneira diferente?
Com base nas respostas postadas para esta pergunta, a situação do construtor padrão parece semelhante. Dado que quase não há diferença de significado entre " =default
" e " {}
" para destruidores, da mesma forma quase não há diferença de significado entre essas opções para construtores padrão? Ou seja, supondo que eu queira criar um tipo em que os objetos desse tipo sejam criados e destruídos, por que eu gostaria de dizer
Widget() = default;
ao invés de
Widget() {}
?
Peço desculpas se estender esta pergunta após a postagem original violar algumas regras de SO. A publicação de uma pergunta quase idêntica para os construtores padrão me pareceu a opção menos desejável.
fonte
= default
é uma imo mais explícita e é consistente com o suporte a ela com construtores.std::has_trivial_destructor<Widget>::value
étrue
para o primeiro, masfalse
para o segundo. Quais são as implicações disso também não sei. :)Respostas:
Essa é uma pergunta completamente diferente quando se pergunta sobre construtores e destruidores.
Se o seu destruidor é
virtual
, então a diferença é insignificante, como Howard apontou . No entanto, se o seu destruidor não era virtual , é uma história completamente diferente. O mesmo se aplica aos construtores.Usar a
= default
sintaxe para funções-membro especiais (construtor padrão, copiar / mover construtores / atribuição, destruidores etc.) significa algo muito diferente de simplesmente fazer{}
. Com o último, a função se torna "fornecida pelo usuário". E isso muda tudo.Esta é uma classe trivial pela definição do C ++ 11:
Se você tentar construir um padrão, o compilador gerará um construtor padrão automaticamente. O mesmo vale para cópia / movimento e destruição. Como o usuário não forneceu nenhuma dessas funções de membro, a especificação do C ++ 11 considera isso uma classe "trivial". Portanto, é legal fazer isso, como copiar o conteúdo para inicializá-lo e assim por diante.
Este:
Como o nome sugere, isso não é mais trivial. Ele possui um construtor padrão fornecido pelo usuário. Não importa se está vazio; No que diz respeito às regras do C ++ 11, esse não pode ser um tipo trivial.
Este:
Novamente, como o nome sugere, esse é um tipo trivial. Por quê? Porque você disse ao compilador para gerar automaticamente o construtor padrão. O construtor, portanto, não é "fornecido pelo usuário". E, portanto, o tipo conta como trivial, pois não possui um construtor padrão fornecido pelo usuário.
A
= default
sintaxe existe principalmente para fazer coisas como construtores de cópia / atribuição, quando você adiciona funções membro que impedem a criação de tais funções. Mas também aciona um comportamento especial do compilador, por isso também é útil em construtores / destruidores padrão.fonte
=default
funções) e fornecidas pelo usuário (que é o caso{}
). As funções declaradas pelo usuário e fornecidas pelo usuário podem impedir a geração de outra função membro especial (por exemplo, um destruidor declarado pelo usuário impede a geração das operações de movimentação), mas apenas uma função especial fornecida pelo usuário torna uma classe não trivial. Certo?= default
parece ser útil para forçar o compilador a gerar um construtor padrão, apesar da presença de outros construtores; o construtor padrão não é declarado implicitamente se forem fornecidos outros construtores declarados pelo usuário.Ambos são não triviais.
Ambos têm a mesma especificação noexcept, dependendo da especificação noexcept das bases e membros.
A única diferença que estou detectando até agora é que, se
Widget
contiver uma base ou membro com um destruidor inacessível ou excluído:Em seguida, a
=default
solução será compilada, masWidget
não será do tipo destrutível. Ou seja, se você tentar destruir aWidget
, receberá um erro em tempo de compilação. Mas se não, você tem um programa de trabalho.Otoh, se você fornecer o destruidor fornecido pelo usuário , as coisas não serão compiladas, independentemente de você destruir ou não um
Widget
:fonte
=default;
o compilador não gera o destruidor a menos que seja usado e, portanto, não gera um erro. Isso me parece estranho, mesmo que não seja necessariamente um bug. Não consigo imaginar que esse comportamento seja obrigatório no padrão.A diferença importante entre
e
é que o construtor padrão definido com
B() = default;
é considerado não definido pelo usuário . Isso significa que, no caso de inicialização de valor como emocorrerá um tipo especial de inicialização que não usará nenhum construtor e, para tipos internos, isso resultará em inicialização zero . Caso
B(){}
isso não ocorra. O Padrão C ++ n3337 § 8.5 / 7 dizPor exemplo:
resultado possível:
http://ideone.com/k8mBrd
fonte