Surcharge des opérateurs d'accès aux membres ->,. *

129

Je comprends plus la surcharge des opérateurs, à l'exception des opérateurs d'accès des membres ->, .*, ->*etc.

En particulier, qu'est-ce qui est passé à ces fonctions d'opérateur et que faut-il renvoyer?

Comment la fonction d'opérateur (par exemple operator->(...)) sait-elle à quel membre il fait référence? Peut-il savoir? A-t-il même besoin de savoir?

Enfin, y a-t-il des considérations const à prendre en compte? Par exemple, lorsque vous surchargez quelque chose comme operator[], vous aurez généralement besoin à la fois d'une version const et non const. Les opérateurs d'accès aux membres nécessitent-ils des versions const et non const?

Bingo
la source
1
Je crois que le C ++ ci-dessus - Faq touche à tous les Q demandés dans le Q. ci-dessus
Alok Save
constet les non- constversions de operator->ne sont pas obligatoires , mais fournir les deux peut être utile.
Fred Foo
1
Voir aussi: yosefk.com/c++fqa/operator.html
György Andrasek
9
@Als: La FAQ n'explique pas comment surcharger ->*et .*. En fait, il ne les mentionne même pas! Je pense qu'ils sont trop rares pour être dans une FAQ, mais je serais heureux de lier cette question à la FAQ. Veuillez ne pas fermer cela comme une dupe de la FAQ!
sbi
@sbi, je n'ai tout simplement pas réussi à trouver un lien vers cette question dans votre (géniale) FAQ, et j'ai fini par poser une question en double. Pourriez-vous le rendre plus évident? (excuses si c'est déjà évident).
P i

Réponses:

144

->

C'est le seul qui soit vraiment délicat. Il doit s'agir d'une fonction membre non statique et ne prend aucun argument. La valeur de retour est utilisée pour effectuer la recherche de membre.

Si la valeur de retour est un autre objet de type classe, pas un pointeur, la recherche de membre suivante est également gérée par une operator->fonction. C'est ce qu'on appelle le «comportement de recherche en avant». Le langage enchaîne les operator->appels jusqu'à ce que le dernier renvoie un pointeur.

struct client
    { int a; };

struct proxy {
    client *target;
    client *operator->() const
        { return target; }
};

struct proxy2 {
    proxy *target;
    proxy &operator->() const
        { return * target; }
};

void f() {
    client x = { 3 };
    proxy y = { & x };
    proxy2 z = { & y };

    std::cout << x.a << y->a << z->a; // print "333"
}

->*

Celui-ci n'est que délicat dans la mesure où il n'y a rien de spécial à ce sujet. La version non surchargée nécessite un objet de type pointeur vers classe sur le côté gauche et un objet pointeur vers type membre sur la droite. Mais lorsque vous le surchargez, vous pouvez prendre tous les arguments que vous aimez et renvoyer tout ce que vous voulez. Il n'est même pas nécessaire que ce soit un membre non statique.

En d' autres termes, celui - ci est juste un opérateur binaire normal comme +, -et /. Voir aussi: Les opérateurs libres -> * surchargent-ils le mal?

.* et .

Ceux-ci ne peuvent pas être surchargés. Il existe déjà une signification intégrée lorsque le côté gauche est de type classe. Il serait peut-être un peu logique de pouvoir les définir pour un pointeur sur le côté gauche, mais le comité de conception du langage a décidé que ce serait plus déroutant qu'utile.

Surcharge -> , ->*, .et .*ne peut remplir dans les cas où une expression serait indéterminée, il ne peut jamais changer le sens d'une expression qui serait valable sans surcharge.

Potatoswatter
la source
2
Votre dernière affirmation n'est pas tout à fait vraie. Par exemple, vous pouvez surcharger l' newopérateur, même s'il est valide même s'il n'est pas surchargé.
Matt
6
@Matt newest toujours surchargé, ou les règles de surcharge ne s'y appliquent pas vraiment (13.5 / 5: Les fonctions d'allocation et de désallocation, opérateur new, opérateur new [], opérateur delete et opérateur delete [], sont décrites en détail en 3.7 .4. les attributs et les restrictions trouvées dans le reste du présent paragraphe ne sont pas applicables à eux , sauf si explicitement indiqué dans 3.7.4.) Mais la surcharge unaire &ou binaire &&, ||ou ,, ou en ajoutant des surcharges operator=ou une surcharge à peu près tout pour une portée définie type d'énumération, peut changer la signification d'une expression. Clarifié la déclaration, merci!
Potatoswatter
41

Opérateur -> est spécial.

"Il a des contraintes supplémentaires atypiques: il doit renvoyer un objet (ou une référence à un objet) qui a également un opérateur de déréférencement de pointeur, ou il doit renvoyer un pointeur qui peut être utilisé pour sélectionner sur quoi pointe la flèche de l'opérateur de déréférencement du pointeur. " Bruce Eckel: Thinking CPP Vol-one: opérateur->

La fonctionnalité supplémentaire est fournie pour plus de commodité, vous n'avez donc pas à appeler

a->->func();

Vous pouvez simplement faire:

a->func();

Cela rend l'opérateur -> différent des autres surcharges d'opérateur.

Totonga
la source
3
Cette réponse mérite plus de crédit, vous pouvez télécharger le livre d'Eckel à partir de ce lien et les informations se trouvent dans le chapitre 12 du volume un.
P i
26

Vous ne pouvez pas surcharger l'accès des membres .(c'est-à-dire la deuxième partie de ce que ->fait). Vous pouvez toutefois surcharger le unaire déréférencement opérateur *(la première partie de ce ->fait).

L' ->opérateur C ++ est fondamentalement l'union de deux étapes et c'est clair si vous pensez que cela x->yéquivaut à (*x).y. C ++ vous permet de personnaliser ce qu'il faut faire avec la (*x)partie quand xest une instance de votre classe.

La sémantique pour la ->surcharge est quelque peu étrange car C ++ vous permet soit de retourner un pointeur régulier (qu'il sera utilisé pour trouver l'objet pointé) soit de renvoyer une instance d'une autre classe si cette classe fournit également un ->opérateur. Lorsque dans ce second cas, la recherche de l'objet déréférencé se poursuit à partir de cette nouvelle instance.

6502
la source
2
Excellente explication! Je suppose que cela signifie la même chose pour ->*, car il est équivalent à la forme de (*x).*?
Bingo du
10

le -> opérateur ne sait pas sur quel membre pointe le membre, il fournit simplement un objet sur lequel effectuer l'accès au membre réel.

De plus, je ne vois aucune raison pour laquelle vous ne pouvez pas fournir de versions const et non const.

John Chadwick
la source
7

Lorsque vous surchargez l'opérateur -> () (aucun argument n'est passé ici), ce que le compilateur fait réellement est d'appeler -> de manière récursive jusqu'à ce qu'il renvoie un pointeur réel vers un type. Il utilise ensuite le membre / méthode correct.

Ceci est utile, par exemple, pour créer une classe de pointeur intelligent qui encapsule le pointeur réel. L'opérateur surchargé-> est appelé, fait tout ce qu'il fait (par exemple le verrouillage pour la sécurité des threads), retourne le pointeur interne, puis le compilateur appelle -> pour ce pointeur interne.

En ce qui concerne constness - il a été répondu dans les commentaires et autres réponses (vous pouvez et devez fournir les deux).

Asaf
la source