6
votes

Opérateur de surcharge [] pour un vecteur clairsemé

J'essaie de créer une classe de vecteur "rare" en C ++, comme: xxx

interne, il sera représenté par un std :: map (où v est le type de valeur stocké). Si un élément n'est pas présent sur la carte, nous prétendons que cela soit égal à la valeur par défaut à partir de l'argument de modèle.

Cependant, j'ai du mal à surcharger l'indice. opérateur, [] . Je dois surcharger l'opérateur [] , car je passe des objets de cette classe dans une fonction de boost qui s'attend à ce que [] fonctionne correctement.

La version const est assez simple: Vérifiez si l'index est sur la carte, renvoyez sa valeur si oui, ou défaut sinon.

Cependant, le La version non constitue nécessite que je retourne une référence, et c'est là que je rencontre des ennuis. Si la valeur n'est que lue , je n'ai pas besoin (ni envie) d'ajouter quelque chose à la carte; Mais si c'est écrit , je devais éventuellement mettre une nouvelle entrée dans la carte. Le problème est que le surchargé [] ne sait pas si une valeur est en train d'être lire ou écrit . Il renvoie simplement une référence.

Y a-t-il un moyen de résoudre ce problème? Ou peut-être de travailler autour d'elle?


2 commentaires

Boost :: mappé_vector <> devrait faire quelque chose de similaire - vous pouvez l'étudier pour des idées (ou peut-être simplement l'utiliser).


Cela ne prend pas en charge mes valeurs par défaut, et aussi, j'allais faire cela pour une matrice bidimensionnelle, alors l'utiliser directement est hors de la question. Mais toujours une référence utile!


3 Réponses :


13
votes

Il peut y avoir une truc très simple, mais sinon, je pense que opérateur [] ne doit être attribué que quelque chose qui peut être attribué à V (et converti en V), pas nécessairement une V &. Donc, je pense que vous devez retourner un objet avec un opérateur surchargé = (const V &) , ce qui crée l'entrée dans votre récipient clairsemé.

Vous devrez vérifier la fonction Boost avec son paramètre de modèle, bien qu'une conversion définie par l'utilisateur sur V affecte quelles chaînes de conversion sont possibles, par exemple en empêchant une conversion plus définie par l'utilisateur dans la même chaîne. .


5 commentaires

C'est la seule solution. D'autre part, les objets proxy ne sont pas parfaits non plus - par exemple, si v avait des opérateurs de conversion surchargés, un proxy ne sera pas en mesure de les propager de manière transparente.


Ah vient de tester les vecteurs de boost. On dirait qu'ils se comportent de cette façon aussi. Intéressant.


(Et vecteur n'est pas non plus un conteneur stl.)


Pour l'utilisation de l'algorithme de boost, cela va probablement bien, si on pourrait s'attendre à attraper le résultat avec une variable temporaire (disent un V &) Qu'est-ce qui se passe?


Euh, sans tester cela, je dirais que la défaillance de la compilation (si la référence est non-const), ou une liaison temporaire de la variable (si la référence est constituée). opérateur [] est facultatif dans l'interface de séquence (23.1.1). Ce conteneur ne le met pas en œuvre tel que défini dans ce tableau (renvoyant une lvalue de T et la prise de temps constant amorti comme spécifié dans le tableau 68 et la clause 12 respectivement): algorithmes qui prennent des itérateurs nécessitent uniquement que opérateur * Être assignable de t, pas que cela renvoie t & , et je m'attends à ce que la même fonction de boost non spécifiée utilise [] à la place.



9
votes

Ne laissez pas l'opérateur et la mise en œuvre non-consensent une référence, mais un objet proxy. Vous pouvez ensuite implémenter l'opérateur d'affectation de l'objet proxy pour distinguer les accès de lecture à l'opérateur [] d'accès à l'écriture.

Voici quelques croquis de code pour illustrer l'idée. Cette approche n'est pas jolie, mais bien - ceci est C ++. Les programmeurs C ++ ne perdent pas de temps en compétition dans des concours de beauté (ils ne seraient pas une chance non plus). ; -) xxx


1 commentaires

Y a-t-il un moyen d'avoir un type être convertible vers E.G. un const int & et un int & et que le compilateur sélectionne le premier chaque fois qu'il peut utiliser une lvalue non rédactrable? J'aimerais avoir un objet proxy renvoyer une référence à un champ chargé de former l'objet principal, puis de la détruire copier le champ à l'objet principal s'il peut avoir changé, mais je ne peux pas comprendre comment éviter la rédaction de l'écriture sur un accès en lecture seule.



1
votes

Je me demande si cette conception est sonore.

Si vous souhaitez renvoyer une référence, cela signifie que les clients de la classe peuvent stocker le résultat de l'appelant opérateur [] dans une référence et lisez-le de / écrire à l'heure ultérieure. Si vous ne renvoyez pas une référence, et / ou n'insérez pas d'élément à chaque fois qu'un index spécifique est adressé, comment pourraient-ils faire cela? (En outre, j'ai le sentiment que la norme nécessite un conteneur STL approprié fournissant opérateur [] pour que cet opérateur renvoie une référence, mais je ne suis pas sûr de cela.)

Vous pourriez être capable de contourner cela en donnant à votre proxy aussi un opérateur V & () (qui créerait l'entrée et attribuerait la valeur par défaut), mais je ne suis pas sûr que ce ne serait pas sûr Il suffit d'ouvrir un autre trou de boucle dans certains cas, je n'avais pas encore pensé.

std :: map résout ce problème en précisant que la version non constituée de cet opérateur insère toujours un élément (et ne fournissant pas une version const du tout).

Bien sûr, vous pouvez toujours dire que n'est pas un conteneur STL hors tension, et opérateur [] ne renvoie pas les références simples Les utilisateurs peuvent stocker. Et peut-être que c'est bon. Je me demande seulement.


2 commentaires

À propos de la solidité de la conception: c'est une optimisation de la mémoire d'un vecteur général, à utiliser dans des cas spécifiques - l'interface / contrat / documentation devrait donc couvrir cela. À propos de la validité des références: Même les itérateurs stl ne sont pas toujours valables.


@xtofl: Cependant, les conteneurs STL définissent très soigneusement quelles opérations peuvent invalider les itérateurs et / ou les références aux éléments du conteneur. Ce conteneur doit faire la même chose, et les utilisateurs doivent s'assurer que tous les modèles utilisant la classe comme paramètre ne font pas de demande qu'il ne peut pas satisfaire.