Por que ++ 11 make C " delete
funções d" participar na resolução de sobrecarga ?
Por que isso é útil? Ou, em outras palavras, por que eles estão ocultos em vez de serem excluídos por completo?
Por que ++ 11 make C " delete
funções d" participar na resolução de sobrecarga ?
Por que isso é útil? Ou, em outras palavras, por que eles estão ocultos em vez de serem excluídos por completo?
Respostas:
Metade do objetivo da = delete
sintaxe é impedir que as pessoas chamem certas funções com determinados parâmetros. Isso é principalmente para evitar conversões implícitas em certos cenários específicos. Para proibir uma sobrecarga específica, ele precisa participar da resolução de sobrecarga.
A resposta que você cita é um exemplo perfeito:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Se delete
removida a função completamente, isso tornaria a = delete
sintaxe equivalente a esta:
struct onlydouble2 {
onlydouble2(double);
};
Você poderia fazer isso:
onlydouble2 val(20);
Isso é C ++ legal. O compilador examinará todos os construtores; nenhum deles aceita um tipo inteiro diretamente. Mas um deles pode assumir após uma conversão implícita. Então vai chamar assim.
onlydouble val(20);
Isso não é C ++ legal. O compilador examinará todos os construtores, incluindo os delete
d. Ele verá uma correspondência exata, via std::intmax_t
(que corresponderá exatamente a qualquer literal inteiro). Portanto, o compilador irá selecioná-lo e imediatamente emitir um erro, porque selecionou uma delete
função d.
= delete
significa "Eu proíbo isso", não apenas "Isso não existe". É uma declaração muito mais forte.
Eu estava perguntando por que o padrão C ++ diz = deletar significa "Eu proíbo isso" em vez de "isso não existe"
É porque não precisamos de gramática especial para dizer "isso não existe". Conseguimos isso implicitamente, simplesmente não declarando o "isso" específico em questão. "Eu proíbo isso" representa uma construção que não pode ser alcançada sem uma gramática especial. Portanto, temos uma gramática especial para dizer "Eu proíbo isso" e não a outra coisa.
A única funcionalidade que você ganharia por ter uma gramática explícita do tipo "isto não existe" seria impedir que alguém posteriormente declarasse sua existência. E isso não é útil o suficiente para precisar de sua própria gramática.
caso contrário, não há como declarar que o construtor de cópia não existe e sua existência pode causar ambiguidades absurdas.
O construtor de cópia é uma função de membro especial. Cada classe sempre tem um construtor de cópia. Assim como eles sempre têm um operador de atribuição de cópia, construtor de movimento, etc.
Essas funções existem; a questão é apenas se é legal chamá-los. Se você tentasse dizer que isso = delete
significava que eles não existiam, a especificação teria que explicar o que significa uma função não existir. Este não é um conceito tratado pela especificação.
Se você tentar chamar uma função que ainda não foi declarada / definida, o compilador irá errar. Mas ocorrerá um erro por causa de um identificador indefinido , não por causa de um erro "função não existe" (mesmo se seu compilador relatar dessa forma). Vários construtores são chamados por resolução de sobrecarga, portanto, sua "existência" é tratada a esse respeito.
Em todos os casos, existe uma função declarada via identificador ou um construtor / destruidor (também declarado via identificador, apenas um identificador de tipo). A sobrecarga do operador oculta o identificador por trás do açúcar sintático, mas ele ainda está lá.
A especificação C ++ não pode lidar com o conceito de uma "função que não existe". Ele pode lidar com uma incompatibilidade de sobrecarga. Ele pode lidar com uma ambigüidade de sobrecarga. Mas não sabe sobre o que não existe. Portanto, = delete
é definido em termos das muito mais úteis "tentativas de chamar isso de falha", em vez da menos útil "fingir que nunca escrevi esta linha".
E novamente, releia a primeira parte. Você não pode fazer isso com "a função não existe". Esse é outro motivo pelo qual é definido dessa forma: porque um dos principais casos de uso da = delete
sintaxe é ser capaz de forçar o usuário a usar certos tipos de parâmetros, para lançar explicitamente e assim por diante. Basicamente, para evitar conversões de tipo implícitas.
Sua sugestão não faria isso.
= delete
significar "este membro não existe", o que implicaria que ele não poderia participar da resolução de sobrecarga.
= delete
quisesse dizer "este membro não existe", então o primeiro exemplo que postei não seria capaz de impedir as pessoas de passarem inteiros para onlydouble
o construtor de , uma vez que a onlydouble
sobrecarga que é excluída não existiria . Ele não participaria da resolução de sobrecarga e, portanto, não o impediria de passar números inteiros. O que é metade do objetivo da = delete
sintaxe: ser capaz de dizer: "Você não pode passar X implicitamente para esta função."
=delete
? Afinal, podemos dizer "não copiável" fazendo exatamente a mesma coisa: declarar o construtor / atribuição de cópia privado. Além disso, observe que declarar algo privado não o torna inviável; o código dentro da classe ainda pode chamá-lo. Portanto, não é o mesmo que = delete
. Não, a = delete
sintaxe nos permite fazer algo que antes era altamente inconveniente e inescrutável de uma forma muito mais óbvia e razoável.
O C ++ Working Draft 2012-11-02 não fornece uma justificativa por trás dessa regra, apenas alguns exemplos
8.4.3 Definições excluídas [dcl.fct.def.delete]
...
3 [ Exemplo : É possível forçar inicialização não padrão e inicialização não integral com
struct onlydouble {
onlydouble() = delete; // OK, but redundant
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
- end example ]
[ Exemplo : pode-se evitar o uso de uma classe em certas novas expressões usando definições excluídas de um operador declarado pelo usuário novo para essa classe.
struct sometype {
void *operator new(std::size_t) = delete;
void *operator new[](std::size_t) = delete;
};
sometype *p = new sometype; // error, deleted class operator new
sometype *q = new sometype[3]; // error, deleted class operator new[]
- exemplo final ]
[ Exemplo : Pode-se tornar uma classe não copiável, ou seja, apenas mover, usando definições excluídas do construtor de cópia e operador de atribuição de cópia e, em seguida, fornecendo definições padrão do construtor de movimento e operador de atribuição de movimento.
struct moveonly {
moveonly() = default;
moveonly(const moveonly&) = delete;
moveonly(moveonly&&) = default;
moveonly& operator=(const moveonly&) = delete;
moveonly& operator=(moveonly&&) = default;
~moveonly() = default;
};
moveonly *p;
moveonly q(*p); // error, deleted copy constructor
- exemplo final ]