web-dev-qa-db-fra.com

Y a-t-il une raison d'utiliser std :: map :: emplace () au lieu de try_emplace () en C ++ 1z?

En C++ 17, std::map et std::unordered_map a obtenu un nouveau modèle de fonction membre: try_emplace() . Ce nouvel ajout, proposé dans n4279 , se comporte de la même manière que emplace() , mais présente les avantages suivants:

  • try_emplace() ne se déplace pas des arguments rvalue si l'insertion ne se produit pas. Ceci est utile lors de la manipulation de cartes dont les valeurs sont des types à déplacement uniquement, tels que std::unique_ptr .
  • try_emplace() traite la clé et les arguments du mapped_type séparément, ce qui le rend un peu plus intuitif que les mutateurs génériques exprimés en termes de value_type (qui est std::pair ).

Compte tenu des avantages ci-dessus, utiliseriez-vous jamais emplace() de C++ 11 au lieu de try_emplace() de C++ 1z lors de l'écriture de code C++ 1z uniquement?

27
s3rvac

try_emplace peut en effet remplacer la plupart des utilisations de emplace, mais si vous avez un cas d'utilisation inhabituel d'un map avec un type de clé non copiable et inamovible, try_emplace ne fonctionnera pas car il copie ou déplace la clé. Dans ce cas, vous devez utiliser emplace avec std::pair's constructeur de construction par morceaux pour éviter les copies et les déplacements.

Même si votre type de clé est copiable et/ou mobile, la construction par morceaux est le seul moyen d'éviter de copier ou de déplacer la construction de la clé, il peut donc y avoir des cas où vous préférez cela à try_emplace.

28
Praetorian

try_emplace Ne prend pas non plus en charge la recherche hétérogène - il ne le peut pas, car il prend la clé.

Supposons que nous ayons un std::map<std::string, int, std::less<>> counts; Et un std::string_viewsv. Je veux faire l'équivalent de ++counts[std::string(sv)];, mais je ne veux pas créer un std::string Temporaire, ce qui est juste du gaspillage, surtout si la chaîne est déjà présente dans la carte. try_emplace Ne peut pas vous aider. Au lieu de cela, vous feriez quelque chose comme

if(auto lb = counts.lower_bound(sv); lb != counts.end() && lb->first == sv) {
    ++lb->second;
}
else {
    counts.emplace_hint(lb, sv, 1);
}
10
T.C.

Je préférerais toujours try_emplace à emplace. Une différence cruciale est que try_emplace ne construira pas l'objet associé à la clé, si la clé existe déjà, ce qui augmentera les performances au cas où des objets de ce type coûteraient cher à créer

Par exemple, le code ci-dessous (exemple de https://github.com/PacktPublishing/Cpp17-STL-Cookbook/blob/master/Chapter02/efficient_insert_or_reassign_to_map.cpp )

#include <iostream>
#include <functional>
#include <list>
#include <map>

using namespace std;

struct billionaire {
    string name;
    double dollars;
    string country;
};

int main()
{
    list<billionaire> billionaires {
        {"Bill Gates", 86.0, "USA"},
        {"Warren Buffet", 75.6, "USA"},
        {"Jeff Bezos", 72.8, "USA"},
        {"Amancio Ortega", 71.3, "Spain"},
        {"Mark Zuckerberg", 56.0, "USA"},
        {"Carlos Slim", 54.5, "Mexico"},
        // ...
        {"Bernard Arnault", 41.5, "France"},
        // ...
        {"Liliane Bettencourt", 39.5, "France"},
        // ...
        {"Wang Jianlin", 31.3, "China"},
        {"Li Ka-shing", 31.2, "Hong Kong"}
        // ...
    };

    map<string, pair<const billionaire, size_t>> m;

    for (const auto &b : billionaires) {
        auto [iterator, success] = m.try_emplace(b.country, b, 1);

        if (!success) {
            iterator->second.second += 1;
        }
    }


    for (const auto & [key, value] : m) {
        const auto &[b, count] = value;

        cout << b.country << " : " << count << " billionaires. Richest is "
        << b.name << " with " << b.dollars << " B$\n";
    }
}

Pour le code ci-dessus

m.try_emplace(b.country, b, 1);

si l'insertion échoue, les paires ne seront pas construites, ce qui augmente les performances

4
Hariom Singh