0
votes

Façon de remplacer l'élément constant dans une carte à l'aide d'itérateur

J'ai un code comme celui-ci:

all_data.erase(it);
all_data.emplace(key, std::move(newData)); // should never fail


2 commentaires

" ça marche bien si const est supprimé. " Je suppose que cela nous amène à la question évidente: étiez-vous sérieux lorsque vous avez déclaré que les données était const ? Parce que si vous voulez changer une chose, cela signifie que vous n'étiez pas vraiment sérieux lorsque vous avez déclaré que cette chose était const . Parce que const signifie que vous ne pouvez pas le changer. Vous avez contraigné vous-même et C ++ n'aime pas vraiment quand vous le faites.


Je ne veux pas que les membres individuels soient modifiés. Cela ne me dérange pas que l'objet entier soit remplacé. Malheureusement, C ++ const combine les deux concepts ensemble. (Qui admet une autre option, à l'aide d'une enveloppe immuable non-Const (par exemple, std :: unique_ptr , avec le pointeur lui-même non-const). Mais cela ajoute une indirection gênante à l'accès , alors je considère que ça pire.)


3 Réponses :


0
votes

Après une autre expérimentation, je pense que la meilleure méthode actuelle est actuellement disponible pour ce scénario: xxx

Cela ne semble toujours pas idéal pour moi car il réaffectera toujours le noeud de la carte . Ce n'est pas la fin du monde mais j'aimerais trouver un moyen de l'éviter si possible.


1 commentaires

Le moyen d'éviter que c'est de suivre les conseils de Nicol et de rendre l'élément de données mutable sur cette carte. Ceci est sémantiquement correct pour votre cas d'utilisation. Fournira une réponse à l'illustration.



2
votes

const signifie immuable, non partiellement mutable. Si vous utilisez const sur une déclaration d'objet (qui est ce que vous faites lorsque vous collez const dans ce paramètre de modèle), C ++ vous croit signifie . Et cela vous retiendra.

SO Const Data est un non-démarreur.

de votre question, je suppose que Data a certaines fonctions qui définissent certaines parties de son état et que vous ne souhaitez pas que les utilisateurs appellent ces fonctions. Mais vous voulez que l'utilisateur puisse écraser la valeur.

Le moyen de faire cela consiste à fournir un type d'objet qui enveloppe une instance data , permettant une attribution à partir d'un objet de données . Ce wrapper fournira des versions des const des données fournit. Mais le type ne fournit pas autrement des modificateurs non const de données .


8 commentaires

C'est ce que j'ai suggéré dans mon commentaire, oui. Mais cela semble également être un non-démarreur, en raison de la cruft supplémentaire indirecte (soit par explicitement à avoir à * ou -> indirect ou implémenter une emballage qui déléguette tous les appels de méthodes à une instance interne non-Const). "Remplacez l'objet que ce nœud de carte pointe sans modifier l'objet d'origine" semble être une opération qui devrait exister, cependant.


@Miral: " C'est ce que j'ai suggéré dans mon commentaire, oui. " Non, votre commentaire suggère un unique_ptr , qui nécessite une allocation de tas. Je parle d'un wrapper qui contient` une instance data ne pointe pas à un. "* Remplacez quel objet de la carte pointe sans modifier l'objet d'origine " qui est une contradiction; Vous ne pouvez pas remplacer un objet sans modifier. Pas à moins que vous ne le détruisiez, puis vous entrez dans STD :: Blanchissez Territoire.


Un objet qui a été constant pour toute sa vie est toujours autorisé à faire appeler son destructeur. Pourquoi la carte ne peut-elle pas détruire l'ancien objet et construire un nouveau à sa place sans violation de la constance?


@Miral: C'est pourquoi .


Je ne pense pas que cet argument s'applique lorsque nous parlons de quelque chose à l'intérieur d'un conteneur d'exécution. Le compilateur ne peut pas faire d'hypothèses sur les valeurs qu'il contient de toute façon.


@Miral: " Je ne pense pas que cet argument s'applique lorsque nous parlons de quelque chose à l'intérieur d'un conteneur d'exécution. " Un conteneur d'exécution qui est requis par la norme à contenir Paire Valeurs. Si cette valeur est est const , alors vous avez besoin de blanchissez mécanique afin de le détruire et de le recréer. Et par "vous", je veux dire n'importe quel code qui tente d'accéder à la carte mappe :: valeur_type , telle que toute interface utilisant des itérateurs. Oh, et opérateur [] doit renvoyer une valeur , alors comment une valeur et provoquer un cycle de détruire / reconstruire quand même?


Oui, mais ils ne sont pas sur la pile, ils sont derrière un itérateur, qui est un pointeur pour la mémoire en tas (et généralement pas directement à une paire mais à un nœud de conteneur). Soit la valeur dans la paire ou la paire entière pourrait être détruite et reconstruite. Ce n'est même pas si différent de ce qui se passe dans ma réponse, sauf que cela saute une réaffectation de la mémoire. (Quel allocator de piscine pourrait finir par utiliser le même stockage pour toute façon.)


Laissez-nous Continuez cette discussion en chat .



1
votes

Dans votre cas, ce que vous voulez vraiment pouvoir faire est de remplacer de manière conditionnelle l'élément de données.

Cela discute fortement pour un élément de données mutable.

Dans le cas général, le conteneur doit Avoir des éléments immuables, vous pourriez donc fournir une enveloppe qui fournit un accès Cons à sauf si un accès mutable est spécifiquement demandé: xxx


4 commentaires

C'est faisable, mais il faut reproduire la majeure partie de l'interface std :: map (et via délégation, pas à l'aide de , car les types sont différents). Cela ne semble pas être une amélioration.


@Miral Je n'ai pas beaucoup d'informations pour continuer de votre cas d'utilisation. J'imagine que cela coûterait environ 30 minutes pour ajouter les interfaces nécessaires dont vous avez besoin. En contrepartie, vous obtenez une performance d'exécution améliorée. Supprimer un nouvel ajout d'un article à un made peut être peu coûteux. tous mes vœux.


Je l'ai essayé avec héritage privé de carte à la place; Cela a permis à l'aide de 10 ( mappe , effacer , insertion_or_assign , EMPACE , < Code> Effacer , vide , Taille , cbiegin , cend , swap ) et déléguant 3 ( commencer , fin , trouver , pour masquer leurs versions non constituées), ce qui n'était pas trop terrible. Ce n'est toujours pas l'interface de carte complète, mais elle suffit à mes besoins.


En fait, je devais déléguer effacer , emplace et insert_or_assign également, sinon ils pourraient éteindre un itérateur non constitué dans leur valeur de retour. Idk, cela semble toujours être quelque chose qui devrait déjà être dans la bibliothèque.