7
votes

Évitez les temporaires dans STD :: MAP / STD :: ONUOMKED_MAP Recherche avec STD :: String Key

Considérez le code suivant:

std::map<std::string, int> m1;
auto i = m1.find("foo");

const char* key = ...
auto j = m1.find(key);


7 commentaires

Si vous pouviez modifier la définition de la carte, vous pouvez utiliser un comparateur personnalisé qui permet Char Cons * directement


Où obtiendriez-vous un const char * ?


@Pablo: Mais alors .. Quelqu'un devra gérer une mémoire où ces clés pointeront. Sauf si elles sont toutes dans le segment de données ...


@Pablo Oui et vous devrez gérer vous-même des chaînes brutes sur le tas. C'est là que je suis en ce moment. Je veux me débarrasser autant de code personnalisé que possible sans sacrifier la performance.


Je ne voulais pas modifier la définition de la carte pour contenir Char Const * , je voulais modifier le comparateur pour autoriser stocker std :: string et comparer avec Char Cons * sans créer de STD :: String


Nevermind .. comme @vladlazarenko a déclaré ci-dessous, vous trouverez une touche de toute façon, le comparateur personnalisé ne verra même pas l'argument original chartons * .


C ++ 14 introduit des «comparateurs transparents» pour permettre des opérations de comparaison hétérogènes, par ex. entre std :: string et un pointeur de caractère.


5 Réponses :


3
votes

Ne pas utiliser les pointeurs; Au lieu de cela, passez directement des chaînes. Ensuite, vous pouvez profiter des références:

void do_something(std::string const & key)
{
    auto it = m.find(key);
    // ....
}


7 commentaires

... sauf si vous n'avez pas besoin d'écrire C. mais alors .. Pourquoi avez-vous besoin de C ++? :-RÉ


Bien que cela ne traite pas de M1.Find ("FOO") Création d'un problème d'objet temporaire (même si, finalement, ce temporaire ne peut pas être contourné).


Je ne peux pas éviter d'utiliser des pointeurs. Je recherche une valeur qui provient de la source externe en tant que chaîne C. Cela signifie toujours que je dois construire une chaîne à chaque recherche.


@ Andrécaron: Pourquoi auriez-vous jamais besoin de trouver un littéral à chaîne? Dans ce cas, il suffit de faire une constante globale std :: string const foo_string ("foo") et utilisez-le ...


@ALEXB: interfaces propres. Faites la chaîne une fois à la limite de la bibliothèque externe, puis réutilisez cette chaîne. Vous n'avez pas de question pure c ++, donc c'est un peu trompeur.


@Kerreksb Il y a une nouvelle chaîne est à chaque fois. Considérez-le comme un grand flux d'entre eux. La recherche n'est faite qu'une fois, lorsque je tire une nouvelle chaîne de ce "flux".


@Kerreksb: Peu importe pourquoi quelqu'un voudrait faire cela (bien que moins de taper se vienne à l'esprit). Peut-être que votre réponse devrait prendre le formulaire "Vous ne pouvez pas contourner le temporaire, mais en faisant ... vous pouvez vraiment atténuer le coût de la création d'un std :: string instance". Ceci bien sûr, sortez la fenêtre après le dernier commentaire d'Alex.



0
votes

Eh bien, Recherche accepte réellement une référence constante à la touche, de sorte que vous ne pouvez pas éviter de le créer à un point ou un autre.

Pour la première partie du code, vous pouvez avoir une STD statique constante :: String avec valeur "FOO" à la recherche. De cette façon, vous ne créerez pas de copies.

Si vous souhaitez aller voir SPARTAN, vous pouvez toujours créer votre propre type pouvant être utilisé comme une chaîne, mais peut également contenir le pointeur sur les littéraux de chaîne.

Mais en tout état de cause, les frais généraux associés à la recherche de cartes sont si énormes, donc cela n'a pas vraiment de sens. Si j'étais vous, je voudrais d'abord remplacer la carte / unommod_map avec le hachage dense de Google. Ensuite, je dirigerais Vtutune d'Intel (amplificateur ces jours-ci) et je verrais où le temps allait et optimise ces endroits. Je doute des chaînes que les clés apparaîtront dans une liste des dix top dix.


2 commentaires

Je dois manquer quelque chose, mais qui est Spartan?


@Cameron: Je crois qu'il est l'auteur du compilateur Cegean C. Très frugal et industrieux.



0
votes

Regardez le stringref classe de LLVM.

Ils peuvent être construits très pas chers à partir de C-Strings, littéraux à cordes ou STD :: String. Si vous avez créé une carte de ceux-ci, au lieu de STD :: String, la construction serait très rapide.

C'est un système très fragile cependant. Vous devez être sûr que quelle que soit la source des chaînes que vous insérez des séjours vivants et non modifiés pour la durée de vie de la carte.


0 commentaires

2
votes

Vous pouvez éviter le temporaire en donnant le std :: map code> une classe de comparaison personnalisée, qui peut comparer char * code> s. (La valeur par défaut utilisera l'adresse du pointeur, qui n'est pas ce que vous voulez. Vous devez comparer la valeur de la chaîne.)

Ainsi, quelque chose comme: P>

class StrCmp
{
public:
  bool operator () (const char *a, const char *b)
  {
    return strcmp(a, b) < 0;
  }
};

// Later:
std::map<const char *, int, StrCmp> m;


2 commentaires

Au Downvoter: l'esprit expliquant votre bowvote?


Vos mots sonnent comme le comparateur personnalisé fonctionne avec std :: map mais votre exemple n'est pas. Qu'avez-vous l'intention de suggérer?



1
votes

Il n'y a aucun moyen d'éviter une instance temporaire std :: string qui copie les données de caractères. Notez que ce coût est très faible et n'aboutit pas une allocation de mémoire dynamique si votre implémentation de la bibliothèque standard utilise des optimisations de chaîne courtes. Toutefois, si vous avez besoin de chaînes de style C proxy sur une base fréquente, vous pouvez toujours trouver personnalisé Des solutions qui passent par-réussir cette allocation. Cela peut payer si vous devez faire cela vraiment souvent et vos chaînes sont suffisamment longues pour ne pas bénéficier des optimisations de chaîne courtes.

Si vous n'avez besoin que d'un très < / em> petit sous-ensemble de la fonctionnalité de chaîne (par exemple, une seule affectation et des copies), vous pouvez alors écrire une petite classe de chaîne à usage spécial qui stocke un pointeur Const Char * et une fonction pour libérer la mémoire. < / p> xxx

alors, vous pouvez utiliser cette classe comme suit: xxx

pas cher_string instance Ne pas créer une copie du tampon de caractères comme std :: string fait, mais il conserve une sémantique de copie sûre pour stocker des instances de pas cher_string dans des conteneurs standard.

notes : Si votre implémentation n'utilise pas l'optimisation de la valeur de retour, vous souhaitez trouver une autre syntaxe de la méthode proxy , telle qu'un constructeur avec une surcharge spéciale ( Prendre une personnalisation proxy_t type à la std :: Nothrow pour le placement nouveau).


2 commentaires

... Mais par votre définition de cheap_string , ne tenterait-il pas de (incorrectement) gratuit "foo" une fois que le temporaire a été supprimé?


Non, parce que la méthode proxy () utilise le Deleter personnalisé Abandon () qui est un non-OP qui n'appelle jamais SUPPRE. Laissez-moi élaborer.