std::map insert() indicateur d'emplacement: différence entre le c++98 et c++11

Sur l'entrée cplusplus sur map::insert() j'ai lu à propos de l'emplacement que l'on pourrait ajouter comme indice pour la fonction que la "fonction optimise son temps d'insertion Si position pointe vers l'élément qui précédera l'élément inséré" pour c++98, tandis que pour c++11 l'optimisation se produit "si position pointe vers, si ce serait le dernier)".

Cela signifie-t-il que les performances des extraits de code du la forme suivante (qui est abondante dans le code hérité sur lequel je travaille et modélisée après le "STL efficace" de Scott Meyer, Article 24) a été affectée par le passage à un compilateur compatible C++11?

auto pLoc = someMap.lower_bound(someKey);
if(pLoc != someMap.end() && !(someMap.key_comp()(someKey, pLoc->first)))
    return pLoc->second;
else
    auto newValue = expensiveCalculation();
    someMap.insert(pLoc, make_pair(someKey, newValue));  // using the lower bound as hint
    return newValue;

Quelle serait la meilleure façon d'améliorer ce modèle pour une utilisation avec C++11?

21
demandé sur mr_T 2015-09-24 13:02:11

4 réponses

La spécification C++98 est un défaut de la norme. Voir la discussion dans LWG question 233 et N1780.

Rappelons que lower_bound renvoie un itérateur au premier élément avec une clé non inférieure à la clé spécifiée, tandis que upper_bound renvoie un itérateur au premier élément avec une clé supérieure à la clé spécifiée. S'il n'y a pas de clé équivalente à la clé spécifiée dans le conteneur, alors lower_bound et upper_bound renvoient la même chose - un itérateur à l'élément qui serait après clé si elle était dans la carte.

Donc, en d'autres termes, votre code actuel fonctionne déjà correctement sous la spécification C++11, et en fait serait faux sous la spécification défectueuse de C++98.

9
répondu T.C. 2015-09-24 10:32:32

Oui, cela affectera la complexité. Donner l'indice correct fera insert() avoir amorti la complexité constante, tandis que donner et l'indice incorrect forcera la carte à rechercher la position depuis le début, donnant la complexité logarithmique. Fondamentalement, un bon indice fait que l'insertion se produit en temps constant, quelle que soit la taille de votre carte; avec un mauvais indice, l'insertion sera plus lente sur des cartes plus grandes.

La solution est, apparemment, de rechercher l'indice avec upper_bound au lieu de lower_bound.

4
répondu SingerOfTheFall 2015-09-24 10:12:07

Je pense que l'insertion correcte de l'indice de style C++11 pourrait être la suivante:

iterator it = table.upper_bound(key);   //upper_bound returns an iterator pointing to the first element that is greater than key
if (it == table.begin() || (--it)->first < key) {
    // key not found path
    table.insert(it, make_pair(key, value));
}
else {
    // key found path
    it->second = value;
}
0
répondu Lihui Zhou 2017-08-18 05:04:14

Un instantané de la fonction lambda de travail pour votre référence.

    auto create_or_get_iter = [this] (const K& key) {

        auto it_upper = m_map.upper_bound(key);
        auto it_effective = it_upper == m_map.begin() ? it_upper : std::prev(it_upper);
        auto init_val = it_effective->second;

        if (it_effective == m_map.begin() || it_effective->first < key) {
            return m_map.insert(it_effective, std::make_pair(key, init_val));
        } else {
            it_effective->second = init_val;
            return it_effective;
        }

    };
0
répondu Chih-Chen Kao 2018-08-12 14:28:42