Selon Bjarne Stroustrup, des références ont été introduites dans C ++ pour soutenir la surcharge de l'opérateur:
Les références ont été introduites principalement pour soutenir la surcharge de l'opérateur. p>
C passe tous les arguments de la fonction par valeur et où le passage d'un objet par valeur serait inefficace ou inapproprié l'utilisateur peut passer un pointeur. Cette stratégie ne fonctionne pas lorsque la surcharge de l'opérateur est utilisée. Dans ce cas, la commodité notionnelle est essentielle car les utilisateurs ne peuvent pas s'attendre à insérer l'adresse des opérateurs si les objets sont grands. Par exemple: p>
class x { public: int a; class y * p; void operator = (class x *); };
5 Réponses :
IME, conversion automatique est le fléchissement de C ++ . Écrivez tous des courriels reconnaissants à Bjarne Stroustrup pour ne pas ajouter de conversion automatique de l'objet au pointeur. p>
Si vous recherchez une raison technique: en C ++, la soustraction des pointeurs, même des pointeurs aux types définis par l'utilisateur, est déjà bien définie. De plus, cela conduirait à des ambiguïtés avec des versions surchargées d'opérateurs qui prennent des objets par copie. p>
Pas grand-balancement, seulement si vous essayez d'appeler une fonction qui accepte les pointeurs, mais les arguments sont des objets. Une sorte de décroissance de matry-to-pointeur.
@Fred: Oui, vous avez raison, je devrais éditer cette phrase pour dire " très i> large" large-balayage ". :) code> beaucoup plus approprié.
Mais vous avez exactement le même problème d'ambiguïté avec les références et la surcharge: vous ne pouvez pas avoir deux fonctions void f (foo); code> et
void f (foo &); code> en même temps. Le compilateur ne serait pas en mesure de décider lequel à appeler si vous avez passé une lvalue à
FOO code>.
@Fred: C'est vrai. Eh bien, c'était juste un point mineur ajouté comme une réflexion après-longue ...
@Fred: Mais cette ambiguïté n'existe qu'entre deux fonctions définies par l'utilisateur. Mais opérateur - code> pour les pointeurs existe déjà, donc l'ambiguïté qu'il y aurait inévitable.
@jalf: Comment serait-il inévitable? & B - & B - & A code> signifierait la soustraction de pointeur comme d'habitude, alors que
B - A code> appellerait l'opérateur
défini par l'utilisateur - généré> avec (implicitement généré) des pointeurs (entièrement générés) à
B code> et
A code>.
@Fredoverflow: wow, donc vous dites "Si j'appelle la fonction avec un type autre que les pointeurs, ils sont convertis en pointeurs et la fonction définie par l'utilisateur qui prendra des pointeurs. Mais si j'appelle avec des pointeurs, La fonction intégrée qui prend des pointeurs sera appelée ». Que ... est ... mal b>
@jalf: Oui, mais je pense que vous le rendez plus dangereux et plus effrayant que ce ne serait effectivement :) Encore une fois, cela ne s'appliquerait qu'aux opérateurs et non aux fonctions ordinaires.
@Fredoverflow: convaincre moi. Je pense que cela semble beaucoup plus dangereux et effrayant que possible.
@JAlf: Il n'y avait aucun problème avec le constructeur de copie et l'opérateur d'affectation prenant des arguments const foo * code> avant l'introduction de références, au moins aucune personne que j'ai jamais entendue. Outre cela, je n'ai pas de "preuve du monde réel", bien sûr.
@Fredoverflow: mais a) ceux qui n'ont aucune autre signification, ils peuvent entrer en conflit. Écriture FOO FOO (Somepointer) CODE> n'est pas valide que si un constructeur prenant un pointeur existe. Mais
A - B code> est déjà valable pour les pointeurs déjà et vous introduisiez une autre signification i> pour cela. Et (b), l'opérateur de copie CTOR et d'affectation avant les références vous permet-ils de transmettre des objets et de les faire convertir implicitement en pointeurs que vous proposez?
@jalf: Je crois fermement que la réponse à b) est oui, mais je devrais consulter TD & E pour cela, et je ne l'ai pas ici pour le moment. Permettez-moi de vous revenir ce soir.
@jalf: TD & E n'est pas explicite sur la question, mais à partir de la citation ajoutée de ma question, il semble i> la réponse à b) est oui. La seule façon d'être vraiment sûre d'envoyer un email à Bjarne.
Si vous acceptez cela, cela le fait avec des pointeurs (implicitement ou explicitement) introduirait une sémantique vraiment déroutante (suis-je en train de fonctionner sur le pointeur ou le pointeur?) Alors sans références qui ne laissent que appeler la valeur. (Outre votre Je ne pense pas que les pointeurs sans Par exemple si je veux écrire un opérateur qui prend un sous vos règles proposées deviendrait: L'appel de la valeur peut avoir travaillé et avoir un sens pour des types "simples", et vous pouvez utiliser des pointeurs intelligents pour les cas où vous ne voulez vraiment pas avoir à prendre une copie de chaque opérande. En général, bien que l'idée d'être obligée de copier tout semble très impur par rapport à une approche basée sur la référence. p> p> FOO C = & B - & A; CODE> Exemple Considérez le cas où vous vouliez écrire un opérateur qui utilisait vraiment un pointeur comme l'un de ses arguments)
& code> sur le site d'appel est utilement fonctionnel et certainement mieux que des références. Si vous en faites une fonctionnalité spéciale de
opérateur code> S et uniquement des opérateurs, cela déplace bien le comportement dans le domaine du cas spécial "cryptique". Si vous souhaitez réduire l'aspect "cas spécial" de celui-ci en faisant une fonctionnalité générale, je ne pense pas que cela soit utile ou utile sur l'appel par référence en tant que référence. P>
foo code> et un
void * code> je peux écrire: p>
Opérateur FOO + (FOO * F, VOID * PTR); code>. Le problème que je vois, il serait alors que même si je voulais
PTR code> pour être explicitement un pointeur par "impliquer
& code> règle", il accepterait rien EM> Et il ne semble pas y avoir un moyen pour moi d'interdire la conversion automatique. Donc
double d; Foo f; f = f + d; code> correspondrait à cela, comme cela serait
int i; Foo f; f = f + i; code> potentiellement de deux manières différentes! P>
Mais je n'ai jamais proposé de "tout copier". J'ai proposé d'utiliser de bons vieux pointeurs pour appeler par référence, sans avoir à utiliser l'opérateur & code> sur le site d'appel (qui était la principale préoccupation de Bjarne).
@Fredoverflow - Mise à jour pour montrer pourquoi je ne pense pas que l'idée implicite et code> serait sémantine mieux que les références sont.
Avez-vous un exemple réaliste où vous voudriez passer un pointeur vide sur un opérateur surchargé?
Opérateur <<< / Code> Lorsque vous écrivez dans un flux, par exemple?
@jalf: Oh, c'est un bon exemple! Je suppose que la proposition interdirait simplement plusieurs étapes de conversion. Si un opérateur prend un foo * code>, vous doit i> fournir un
foo code>. Dans le cas d'un
Void * code>, il n'y aurait tout simplement pas de vide code> que vous pourriez passer, car il n'y a pas de
vide code> objet.
Cela ne vous frappe pas aussi étrange que "si un opérateur prend un [x], vous devez le transmettre quelque chose de différent i>"?
@JAlf: Nous avons accepté que les opérateurs postfix prennent un paramètre supplémentaire int code>, mais aucun n'est réellement jamais transmis à eux :)
@Fredoverflow - Je pense que c'était probablement une erreur, mais au moins, ce n'est que pour une chose très localisée plutôt que quelque chose dont vous avez besoin pour être conscient potentiellement n'importe où. (Je ne vois pas pourquoi opérateur ++ postfix code> n'aurait pas été raisonnable même si vous utilisez
++ opérateur code> vs
opérateur ++ code> aurait fait le grammaire vraiment laid)
c ++ vise à être fortement typé, tellement tente d'être cohérente avec cette philosophie, il est logique qu'un objet est-pas-un em> pointeur de cet objet, et je suis totalement d'accord avec SBI sur la façon dont super, il ne faut pas avoir de conversion automatique tout autour (j'ai à l'esprit les projets multi-contributeurs). Pour répondre plus précisément, les personnes apprenant C ++ peuvent être confus au début par des références par rapport au pointeur, mais je ne suis pas Bien sûr, il clarifierait quoi que ce soit pour que ce type de conversion automatique se produise en leur nom.
Par exemple: P> Foo ** operator+(Foo **lhs, Foo **rhs)
{...}
Foo *varFoo1,*varFoo2;
varFoo1 + &varFoo2;
Notez que ma "proposition" ne concerne que les opérateurs et non les méthodes ordinaires, car les opérateurs sont les suivants: Bjarne justifiait la nécessité d'introduire des références.
Les opérateurs étaient où Bjarne Premier i> a rencontré le besoin de références. Simplement résoudre le cas de l'opérateur ne prouve pas que "C ++ aurait été mieux désactivé sans références".
@jalf: Vous n'avez pas besoin de références pour des fonctions ordinaires, car vous pouvez simplement passer des pointeurs. Certaines conventions de codage exigent même que vous passez par le pointeur au lieu de par référence. Cela rend l'intention évidente sur le site d'appel.
@Fredoverflow: Oui, mais mon point est que d'autres utilisations des références ont pu apparaître depuis lors. Donc, afin de montrer que C ++ pourrait fonctionner sans références, vous devrez montrer qu'il n'y a pas d'autres parties de la langue où les références sont un avantage significatif.
@Frederverflow: Pour ma conscience de l'esprit, j'ai tendance à considérer que les opérateurs se comportent vraiment comme une autre méthode, avec la seule option ajoutée qu'elles offrent une "syntaxe d'appel de raccourci" (à la hauteur de vous pour l'utiliser ou pour écrire un appel complet) . J'aurais plus peur que ce casting implicite d'objet au pointeur ne se produise qu'avec des opérateurs. Mais s'en tenir aux opérateurs, se poursuivant avec l'avantage de la consigne. Vous ne pouviez pas écrire une chaîne de concaténation :: opérateur + pour travailler avec: std :: ficelle mess = "humain"; mess + "après tout"; code> avec des pointeurs à la chaîne uniquement. Vous avez besoin de la référence constante ou de la copie d'objet ici
Je ne vois pas comment cela résout le problème: Vous définiriez un opérateur code> pour alors vous auriez besoin de sémantique artificielle qui "quand on l'appelle explicitement avec des pointeurs, l'opérateur arithmétique du pointeur est appelé. Lorsqu'il est appelé avec un objet Les lvalues, ils se décomposent dans des pointeurs, puis la version surchargée est appelée ». Qui, pour être honnête, semble beaucoup plus articulé, et beaucoup moins intuitif que d'ajouter simplement des références. P> puis à l'intérieur de l'opérateur Vous proposez une conversion implicite qui a une sémantique différente de la conversion vous-même. C'est-à-dire: p> effectue une conversion implicite de mais ce code ferait quelque chose de complètement différent: p> puis considère le code de modèle . En général, les conversions implicites font que les modèles sont vraiment douloureux car le type passé peut ne pas être le type que vous souhaitez utiliser. P> Bien sûr, il y a aussi des problèmes plus pratiques: P> Les références permettent des optimisations qui ne sont pas possibles avec des pointeurs. (Le compilateur peut supposer qu'ils ne sont jamais nuls, et il peut être mieux en mesure de faire une analyse d'aliasing) p> A plus de, Code échouerait silencieusement si aucun opérateur et l'un des objectifs avec C ++ était de le rendre générique: pour éviter d'avoir des types ou des fonctions de magie ». Dans le monde entier C ++, les opérateurs sont vraiment seulement fonctionne avec une syntaxe différente. Avec cette règle, ils auraient également une sémantique différente. (Et si l'opérateur est appelé avec la syntaxe de fonction? opérateur - code> appelé les pointeurs a déjà une signification.
foo * code> arguments, mais cela existe déjà. p>
code>, je reçois les arguments comme des pointeurs, puis Je ferais mieux de vous assurer de désirer si je dois appeler un autre opérateur (ou le même) de là. Sinon, je finirais accidentellement à effectuer des arithmétiques du pointeur. P>
foo code> sur
foo * code>, puis une fonction avec la signature < code> barre void (foo *) code> est appelé. p>
foo * code>, et cela appellerait également une fonction avec la barre de signature
Void (FOO *) code>, mais ce serait potentiellement un < Strong> fonction différente forte>. (Dans le cas de
opérateur - code>, par exemple, il s'agirait de l'opérateur surchargé de l'utilisateur, et l'autre serait le pointeur standard arithmétique one. P>
correspondant - code> peut être trouvé. Au lieu de me dire que em>, le compilateur commencerait implicitement à faire des arithmétiques du pointeur. Pas un pot-casseur, mais je n'ai vraiment pas préférez préférer que les erreurs soient attrapées tôt dans la mesure du possible. p>
Opérateur + (A, B) CODE>? P> P>
Il n'y a pas de conversion implicite dans bar (foo) code>, car
bar code> n'est pas un opérateur.
@Fredoverflow: C'était un exemple généralisé. bar code> pourrait être un opérateur.
Comment la non-nullité des références permettrait-elle de meilleures optimisations? Étant donné que la derréférance, le pointeur NULL est un comportement indéfini, je ne vois honnêtement pas comment travailler avec des pointeurs non nuls pourrait être plus lent que de travailler avec des références.
L'argument "Fonction différent" est un boîtier d'angle, non? Ou y a-t-il d'autres exemples en plus de la soustraction du pointeur?
Je ne pense pas que votre argument "faille silencieusement" s'applique. S'il n'y a pas d'opérateur code> défini par l'utilisateur - code>, alors a - b code> est une erreur de compilation, car la conversion ne se produit que comme une étape intermédiaire dans l'argument en passant.
@Fredoverflow: Cela semble être une définition circulaire: la conversion ne se produit que s'il existe une fonction candidate à appeler. Mais afin de déterminer s'il existe une fonction candidate à appeler, vous devez effectuer la conversion. Bien sûr, vous pouvez vous en sortir, mais vous ne rendez pas le travail du compilateur plus facile. Vous définissez efficacement un nouveau type i> de conversions implicites, qui doit s'intégrer avec le gâchis existant des règles de conversion
Je n'ai jamais lu le livre de D & E, mais ma compréhension est que les références n'étaient pas ajoutées pour le rendre meilleur lorsque vous passez des arguments à une fonction, mais pour le rendre meilleur lors de leur apparition de valeurs. L'affectation simple et composée et les opérateurs de l'indice entraînent tous des lvalues lorsqu'ils sont utilisés sur des types intégrés, mais il n'ya aucun moyen de faire la même chose avec des types intégrés sans références.
Je me demande également comment vous alliez sur la mise en œuvre d'un opérateur qui fonctionne à la fois sur un type et un pointeur à un type. p> contre p> La même chose pourrait être montrée avec un seul type, mais simplement dire " que "semble raisonnable dans ce cas ... lorsque plusieurs types sont impliqués, les chances d'introduire accidentellement une ambiguïté augmente. P> p>
pourquoi avez-vous choisi la soustraction comme exemple ?? Il n'a pas besoin de références au travail ...
@YI_H: Je n'ai pas choisi l'exemple de la soustraction, Bjarne a fait. Comment vous i> le résout-il sans références et obtenez-vous encore une sémantique d'appel-référence?
@YI_H: En réalité, je ne peux penser à aucun opérateur qui "a besoin" absolument. Même le préfixe unaire
opérateur * () code> pourrait, syntaxiquement, renvoyer une copie. Modifier B>: Oh, attendez. Je ne peux pas penser à
opérateur + = () code> et les goûts de travailler sans (une forme de) références.
@FredOverflow: "Les références ont été introduites dans C ++ pour soutenir la surcharge de l'opérateur" .. "La solution de Bjarne à ce problème était d'introduire des types de référence dans la langue". C'est ce que vous i> dit, pas Bjarne.
@YI_H: Si vous lisez la conception et l'évolution de C ++ i>, vous trouverez l'argument de Bjarne. Il fait un argument similaire dans une de ses FAQ A >, bien qu'avec l'ajout au lieu de la soustraction:
la raison directe que je les ai introduites en C ++ consistait à soutenir la surcharge de l'opérateur code>
@SBI: Avant l'introduction de types de références dans la langue, l'opérateur d'affectation ait ressemblé à un opérateur
(const foo * rhs); code> qui vous fait se demander pourquoi Bjarne n'est pas resté sur cette route. ..
@Fredoverflow: Eh bien, c'est un exemple stupide et je m'en fiche de la Bjarne.
@YI_H: Voulez-vous expliquer pourquoi l'exemple est stupide? Et quel serait un meilleur exemple? Merci.
@Frederverflow: Comme je l'ai dit, cela fonctionne bien sans références (non aussi efficaces si le compilateur ne peut pas l'optimiser). Pour l'autre q Lisez à nouveau le commentaire de SBI.
Alors, que se passerait-il si je définis mon propre opérateur de conversion de certains types
foo code> sur
foo * code>? Qui serait utilisé lorsque je passe un objet code> objet code> à un opérateur?
Les gens se confondent sur toutes sortes de choses tout le temps, qu'il s'agisse de statistiques, d'évolution ou de femmes. Cela ne signifie pas que l'une de ces choses est elle-même inhéremment. En disant que quelque chose vous confond, ou "confond les gens", n'est guère argumenté pour ou contre quoi que ce soit.
@Kerrek: ... Sauf si la réduction de la confusion sans perte augmente la productivité. Nous sommes dans le domaine de la programmation: la clarté est quelque chose à efforcer.
Regardant par les réponses et vos commentaires, cela me semble que vous aimez fondamentalement réintroduire des références sous une forme plus complexe, avec une syntaxe plus incohérente, et plus de pièges. Manipulation de tous les cas indiqués dans les réponses à l'aide des pointeurs et des règles de conversion spéciales sensibles au contexte et donnant aux opérateurs un statut «spécial» avec une sémantique différente de celle des fonctions régulières beaucoup plus complexes que d'ajouter simplement des références.
@Gman: C'est vrai, mais dans le cas présent, c'est une question très subjective ce qui est déroutant et ce qui n'est pas. La programmation est un sujet complexe. Un certain nombre de choses sont potentiellement déroutantes. La question est la question que les fonctionnalités permettent un programmeur compétent i> de travailler efficacement. Dans le cas des références, on pourrait le trouver même plus i> confusant si les pointeurs et les références étaient tous syntaxiquement indiscernables ... La question de savoir si les références sont inévitables est bonne, je n'aime tout simplement pas l'implication que l'implication que comprenant eux étaient un mauvais choix de conception.
De manière réaliste, cela substituerait simplement un ensemble de questions de programmeur amateur avec un autre ensemble. Au lieu de "Pourquoi ne pouvez-je pas réexaminer les références" Vous obtenez "ne
foo + foo code> copie ses arguments". Au lieu de "Pourquoi ne puis-je pas avoir une gamme de références" vous obtenez "pourquoi
opérateur + (foo *, foo *) code> échoue lors de la fourniture de deux pointeurs".