0
votes

Évaluation d'argument paresseux pour try_emplace?

J'ai besoin d'un objet qui peut encapsuler une initialisation paresseuse pour un try_emplace avec map (en effet, appeler une fonction de type usine uniquement lorsque cela est nécessaire), de sorte que ce qui suit convertira ok en try_emplace :

std::map<std::string, whatever_wrapper<bool> > cache_;
cache_.try_emplace("hello", []{return true;});

ou peut-être

std::map<std::string, bool> cache_;
cache_.try_emplace("hello", lazy_wrapper([]{return true;}));

J'imagine que cela devrait être possible, mais principalement à la recherche d'un solution standard (par exemple, std / boost ) par rapport au roulage de mon propre wrapper.


5 commentaires

Qu'as-tu essayé?


J'ai essayé de fournir le lambda nu, celui ofc. n'a pas compilé. Je suppose qu'il est assez facile de créer un type de classe qui sera converti, mais comme je l'ai dit, je suis surtout intéressé si une solution standard existe déjà.


Hélas dans un monde (de transmission) parfait; P0927R0: Vers A (paresseux) Mécanisme de transfert pour C ++ .


En termes de paresse, voulez-vous dire que la fonction n'est pas appelée à moins que la clé ne soit pas présente dans la carte?


@NicolBolas c'est correct


3 Réponses :


2
votes

Ce qui suit est une solution faite à la main que je viens de mettre en place en quelques minutes, qui fait le travail, mais je suis surtout à la recherche d'une sorte de solution standard:

template<class F>
struct lazy_wrap {
    F f_;
    lazy_wrap(F&& f) : f_(f) {}
    template<class T>
    operator T() {
        return f_();
    }
};


2 commentaires

Cela évaluera f à chaque fois pour obtenir un T , je n'appellerais pas ça "paresseux"


Oh, c'est pour std :: map , pas pour std :: map >



-1
votes

Avez-vous vraiment besoin d'un wrapper? Vous pouvez le faire à la place:

// C++20
if (!cache_.contains("hello"))
    cache_.emplace("hello", [] { return true; });

// pre C++20
if (cache_.count("hello") == 0)
    cache_.emplace("hello", [] { return true; });

Simple, clair, pas de mal de tête.


2 commentaires

cela fait une double recherche


Je n'ai pas vraiment besoin un wrapper comme vous dites (map.find fonctionne aussi) - mais il est plus expressif de cette façon avec une ligne de code. Sur le même token, nous ne besoin try_emplace en premier lieu.



0
votes

Vous devrez finalement implémenter la logique try-emplace vous-même, car il n'y a pas de fonction simple pour le faire.

template<typename Map, typename Key, typename Func>
auto lazy_try_emplace(Map &map, const Key &key, Func f)
{
    auto it = map.find(key);
    if(it == map.end())
      return map.emplace(key, f());
    return std::pair(it, false);
}

Oui, cela recherche l'élément deux fois, mais il n'y a aucun moyen de le faire. évitez cela sans faire partie de l'implémentation de std :: map (c'est pourquoi try_emplace existe). Le temps de recherche peut être réduit en remplaçant map.find par map.lower_bound , en modifiant le test conditionnel pour voir si la clé n'est pas égale à clé , et en utilisant emplace_hint avec cet itérateur.


0 commentaires