Pourquoi C ++ 11 fait-il participer les fonctions « delete
d» à la résolution des surcharges ?
Pourquoi est-ce utile? Ou en d'autres termes, pourquoi sont-ils masqués au lieu d'être entièrement supprimés?
Pourquoi C ++ 11 fait-il participer les fonctions « delete
d» à la résolution des surcharges ?
Pourquoi est-ce utile? Ou en d'autres termes, pourquoi sont-ils masqués au lieu d'être entièrement supprimés?
Réponses:
La moitié du but de la = delete
syntaxe est de pouvoir empêcher les gens d'appeler certaines fonctions avec certains paramètres. C'est principalement pour empêcher les conversions implicites dans certains scénarios spécifiques. Afin d'interdire une surcharge particulière, il doit participer à la résolution de la surcharge.
La réponse que vous citez vous donne un exemple parfait:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Si delete
la fonction était entièrement supprimée, cela rendrait la = delete
syntaxe équivalente à ceci:
struct onlydouble2 {
onlydouble2(double);
};
Vous pouvez faire ceci:
onlydouble2 val(20);
C'est du C ++ légal. Le compilateur examinera tous les constructeurs; aucun d'entre eux ne prend directement un type entier. Mais l'un d'eux peut le prendre après une conversion implicite. Alors ça va appeler ça.
onlydouble val(20);
Ce n'est pas du C ++ légal. Le compilateur examinera tous les constructeurs, y compris les delete
d. Il verra une correspondance exacte, via std::intmax_t
(qui correspondra exactement à n'importe quel littéral entier). Ainsi, le compilateur le sélectionnera et émettra immédiatement une erreur, car il a sélectionné une delete
fonction d.
= delete
signifie «je l'interdis», pas simplement «cela n'existe pas». C'est une déclaration beaucoup plus forte.
Je demandais pourquoi la norme C ++ dit = supprimer signifie "je l'interdis" au lieu de "cela n'existe pas"
C'est parce que nous n'avons pas besoin d'une grammaire spéciale pour dire «cela n'existe pas». Nous obtenons cela implicitement en ne déclarant tout simplement pas le «ceci» en question. «J'interdis cela» représente une construction qui ne peut être réalisée sans une grammaire spéciale. Donc, nous obtenons une grammaire spéciale pour dire "j'interdis cela" et pas l'autre chose.
La seule fonctionnalité que vous gagneriez en ayant une grammaire explicite «cela n'existe pas» serait d'empêcher quelqu'un de déclarer plus tard qu'elle existe. Et ce n'est tout simplement pas assez utile pour avoir besoin de sa propre grammaire.
il n'y a autrement aucun moyen de déclarer que le constructeur de copie n'existe pas et son existence peut provoquer des ambiguïtés absurdes.
Le constructeur de copie est une fonction membre spéciale. Chaque classe a toujours un constructeur de copie. Tout comme ils ont toujours un opérateur d'affectation de copie, un constructeur de déplacement, etc.
Ces fonctions existent; la question est seulement de savoir s'il est légal de les appeler. Si vous essayiez de dire que cela = delete
signifiait qu'elles n'existaient pas, alors la spécification devrait expliquer ce que cela signifie pour une fonction de ne pas exister. Ce n'est pas un concept traité par la spécification.
Si vous essayez d'appeler une fonction qui n'a pas encore été déclarée / définie, le compilateur fera une erreur. Mais cela provoquera une erreur à cause d'un identifiant non défini , pas à cause d'une erreur "la fonction n'existe pas" (même si votre compilateur le signale de cette façon). Différents constructeurs sont tous appelés par résolution de surcharge, leur "existence" est donc gérée à cet égard.
Dans tous les cas, il y a soit une fonction déclarée via un identifiant, soit un constructeur / destructeur (également déclaré via un identifiant, juste un identifiant de type). La surcharge de l'opérateur cache l'identifiant derrière le sucre syntaxique, mais il est toujours là.
La spécification C ++ ne peut pas gérer le concept de «fonction qui n'existe pas». Il peut gérer une incompatibilité de surcharge. Il peut gérer une ambiguïté de surcharge. Mais il ne sait pas ce qui n'est pas là. Ainsi = delete
est défini en termes de "tentatives pour appeler cet échec" bien plus utiles que de "prétendre que je n'ai jamais écrit cette ligne", moins utile.
Et encore une fois, relisez la première partie. Vous ne pouvez pas faire cela avec «la fonction n'existe pas». C'est une autre raison pour laquelle il est défini de cette façon: parce que l'un des principaux cas d'utilisation de la = delete
syntaxe est de pouvoir forcer l'utilisateur à utiliser certains types de paramètres, à effectuer un cast explicite, etc. Fondamentalement, pour déjouer les conversions de type implicites.
Votre suggestion ne ferait pas cela.
= delete
à vouloir dire "ce membre n'existe pas", ce qui impliquerait qu'il ne pourrait pas participer à la résolution des surcharges.
= delete
voulait dire "ce membre n'existe pas", alors le premier exemple que j'ai posté ne pourrait pas empêcher les gens de passer des entiers au onlydouble
constructeur de. , car la onlydouble
surcharge supprimée n'existerait pas . Cela ne participerait pas à la résolution des surcharges et ne vous empêcherait donc pas de passer des entiers. Ce qui est la moitié du point de la = delete
syntaxe: pouvoir dire: "Vous ne pouvez pas passer X implicitement à cette fonction."
=delete
? Après tout, nous pouvons dire "non copiable" en faisant exactement la même chose: déclarer le constructeur de copie / l'affectation privée. Notez également que déclarer quelque chose de privé ne le rend pas impossible; le code dans la classe peut toujours l'appeler. Donc ce n'est pas la même chose que = delete
. Non, la = delete
syntaxe nous permet de faire quelque chose qui était auparavant très gênant et impénétrable auparavant d'une manière beaucoup plus évidente et raisonnable.
Le brouillon de travail C ++ 2012-11-02 ne fournit pas de justification derrière cette règle, juste quelques exemples
8.4.3 Définitions supprimées [dcl.fct.def.delete]
...
3 [ Exemple : On peut imposer une initialisation non par défaut et une initialisation non intégrale avec
struct onlydouble {
onlydouble() = delete; // OK, but redundant
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
- end example ]
[ Exemple : On peut empêcher l'utilisation d'une classe dans certaines nouvelles expressions en utilisant des définitions supprimées d'un opérateur déclaré par l'utilisateur nouveau pour cette 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[]
- exemple de fin ]
[ Exemple : On peut rendre une classe non copiable, c'est-à-dire déplacer uniquement, en utilisant les définitions supprimées du constructeur de copie et de l'opérateur d'affectation de copie, puis en fournissant les définitions par défaut du constructeur de déplacement et de l'opérateur d'affectation de déplacement.
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
- fin d'exemple ]