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?
87
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, viastd::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é unedelete
fonction d.= delete
signifie «je l'interdis», pas simplement «cela n'existe pas». C'est une déclaration beaucoup plus forte.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.
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.
la source
= 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 auonlydouble
constructeur de. , car laonlydouble
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
struct onlydouble { onlydouble() = delete; // OK, but redundant onlydouble(std::intmax_t) = delete; onlydouble(double); };
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[]
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
la source