web-dev-qa-db-fra.com

Surcharge des opérateurs d’accès aux membres ->,. * (C ++)

Je comprends la plupart des surcharges d’opérateurs, à l’exception des opérateurs d’accès membres ->, .*, ->* Etc.

En particulier, qu'est-ce qui est passé à ces fonctions d'opérateur et qu'est-ce qui devrait être retourné?

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

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

114
Bingo

->

C'est le seul vraiment difficile. Il doit s'agir d'une fonction membre non statique et ne nécessite 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, et non un pointeur, la recherche de membre suivante est également gérée par un operator-> une fonction. C'est ce qu'on appelle le "comportement d'exploration". La langue enchaîne les operator-> appelle jusqu'à ce que le dernier retourne 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 est seulement délicat en ce qu'il n'a rien de spécial. La version non surchargée nécessite un objet de type pointeur vers le type de classe à gauche et un objet de type pointeur vers un type de membre à droite. Mais lorsque vous le surchargez, vous pouvez prendre tous les arguments que vous voulez 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: Le libre opérateur -> * surcharge-t-il le mal?

.* et .

Ceux-ci ne peuvent pas être surchargés. Il y a déjà une signification intégrée lorsque le côté gauche est de type classe. Cela aurait peut-être un sens de pouvoir les définir pour un pointeur à gauche, mais le comité de conception linguistique a décidé que ce serait plus déroutant qu’utile.

Surcharge ->, ->*, ., et .* ne peut remplir que dans les cas où une expression serait non définie, elle ne peut jamais changer le sens d’une expression qui serait valide sans surcharge.

131
Potatoswatter

Opérateur -> est spécial.

"Il a des contraintes supplémentaires et atypiques: il doit renvoyer un objet (ou une référence à un objet) qui possède également un opérateur de déréférencement de pointeur, ou un pointeur qui peut être utilisé pour sélectionner le pointeur de la flèche de l'opérateur de déréférencement de pointeur. " Bruce Eckel: Penser 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.

29
Totonga

Vous ne pouvez pas surcharger l’accès des membres . (C’est-à-dire la deuxième partie de ce que -> Fait). Vous pouvez cependant surcharger l'opérateur unaire dereferencing* (C'est-à-dire la première partie de ce que -> Fait).

L'opérateur C++ -> Est fondamentalement l'union de deux étapes. Cela est clair si vous pensez que x->y Est équivalent à (*x).y. C++ vous permet de personnaliser quoi faire avec la partie (*x) Lorsque x est une instance de votre classe.

La sémantique de la surcharge -> Est quelque peu étrange car C++ vous permet de renvoyer un pointeur normal (qui servira à trouver l'objet pointé) ou de renvoyer une instance d'une autre classe si cette classe fournit également un -> Opérateur. Dans ce second cas, la recherche de l'objet déréférencé se poursuit à partir de cette nouvelle instance.

23
6502

L'opérateur -> Ne sait pas sur quel membre est pointé, il fournit simplement un objet sur lequel effectuer l'accès réel au membre.

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

9
John Chadwick

Lorsque vous surchargez l'opérateur -> () (aucun argument n'est passé ici), le compilateur appelle en fait -> récursivement jusqu'à ce qu'il renvoie un pointeur réel sur un type. Il utilise ensuite le bon membre/la bonne méthode.

Cela 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 ce qu'il fait (par exemple, verrouille pour la sécurité du thread), retourne le pointeur interne puis le compilateur appelle -> pour ce pointeur interne.

En ce qui concerne la constance, les commentaires et d’autres réponses y ont répondu (vous pouvez et devez fournir les deux).

6
Asaf