Question std :: map <> :: insert en utilisant des objets non copiables et une initialisation uniforme


Regardez le code suivant:

#include <utility>
#include <map>

// non-copyable but movable
struct non_copyable {
    non_copyable() = default;

    non_copyable(non_copyable&&) = default;
    non_copyable& operator=(non_copyable&&) = default;

    // you shall not copy
    non_copyable(const non_copyable&) = delete;
    non_copyable& operator=(const non_copyable&) = delete;
};

int main() {
    std::map<int, non_copyable> map;
    //map.insert({ 1, non_copyable() });  < FAILS
    map.insert(std::make_pair(1, non_copyable()));
    // ^ same and works
}

La compilation de cet extrait échoue lorsque vous décommentez la ligne marquée sur g ++ 4.7. L'erreur produite indique que non_copyable ne peut pas être copié, mais je m'attendais à ce qu'il soit déplacé.

Pourquoi l'insertion d'un std::pair construit en utilisant une initialisation uniforme échoue mais pas un construit en utilisant std::make_pair? Les deux ne sont-ils pas censés produire des valeurs pouvant être déplacées avec succès sur la carte?


10
2018-02-16 23:51


origine


Réponses:


[Ceci est une réécriture complète. Ma réponse précédente n'avait rien à voir avec le problème.]

le map a deux insert surcharges:

  • insert(const value_type& value), et

  • <template typename P> insert(P&& value).

Lorsque vous utilisez l'initialiseur de liste simple map.insert({1, non_copyable()});, toutes les surcharges possibles sont prises en compte. Mais seul le premier (celui qui prend const value_type&) est trouvé, puisque l’autre n’a pas de sens (il n’ya aucun moyen de deviner par magie que vous vouliez créer une paire). La première surcharge ne fonctionne pas bien sûr puisque votre élément n'est pas copiable.

Vous pouvez faire fonctionner la deuxième surcharge en créant la paire explicitement, soit avec make_pair, comme vous l'avez déjà décrit, ou en nommant explicitement le type de valeur:

typedef std::map<int, non_copyable> map_type;

map_type m;
m.insert(map_type::value_type({1, non_copyable()}));

Maintenant l'initialiseur de liste sait rechercher map_type::value_type constructeurs, trouve le mobile approprié, et le résultat est une paire de valeurs qui se lie à la P&&surcharge de la insert fonction.

(Une autre option est d'utiliser emplace() avec piecewise_construct et forward_as_tuple, bien que cela devienne beaucoup plus verbeux.)

Je suppose que la morale ici est que les initialiseurs de listes recherchent des surcharges viables - mais ils doivent savoir quoi chercher!


17
2018-02-17 00:05