web-dev-qa-db-fra.com

Est-il acceptable de "déplacer" un objet d'une file d'attente, si vous êtes sur le point de le quitter?

Je travaille sur un analyseur syntaxique pour commands (qui sont des wrappers sophistiqués autour de grands tableaux de données) et une file d'attente sur laquelle résident les commandes non gérées. Si j'ai besoin d'une commande, je l'interroge avec le code suivant:

boost::optional<command> get_command() {
    if (!has_command()) return boost::optional<command>(nullptr);
    else {
        boost::optional<command> comm(command_feed.front()); //command_feed is declared as a std::queue<command>
        command_feed.pop();
        return comm;
    }
}

Le problème est que ces commandes peuvent avoir une taille en mégaoctets, dans les bonnes circonstances, et nécessitent une analyse rapide. Je pensais que je pouvais optimiser le transfert pour un déménagement comme suit:

boost::optional<command> get_command() {
    if (!has_command()) return boost::optional<command>(nullptr);
    else {
        boost::optional<command> comm(std::move(command_feed.front())); //command_feed is declared as a std::queue<command>
        command_feed.pop();
        return comm;
    }
}

Et cela semble fonctionner pour ce cas particulier, mais cela peut-il être utilisé comme solution polyvalente à tout objet RAII correctement entretenu, ou devrais-je faire autre chose?

16
Xirema

Oui, c'est parfaitement sûr:

std::queue<T> q;
// add stuff...

T top = std::move(q.front());
q.pop();

pop() n'a aucune condition préalable sur le premier élément de la q ayant un état spécifié, et puisque vous n'utilisez pas par la suite q.front(), vous n'avez plus besoin d'invalider cet objet. 

Cela semble être une bonne idée!

26
Barry

Cela dépend de ce que le constructeur de mouvements pour votre type fait. Si l'objet d'origine se trouve dans un état pouvant être détruit en toute sécurité, tout va bien. Si non, alors vous pouvez être en difficulté. Notez que les commentaires sur les conditions préalables et les états valides concernent les contraintes sur les types définis dans la bibliothèque standard . Les types que vous définissez n'ont pas ces contraintes, sauf dans la mesure où ils utilisent des types de la bibliothèque standard. Examinez donc le constructeur de votre déménagement pour déterminer ce que vous pouvez et ne pouvez pas faire avec un objet déplacé.

6
Pete Becker

Oui . Tant que l'argument de modèle de conteneur de votre std::queue garantit qu'il n'y a aucune condition préalable sur l'état de ses valeurs contenues pour pop_front(); La valeur par défaut pour std::queue est std::deque et offre la garantie.

Tant que vous vous assurez de ce que j'ai écrit dans le paragraphe précédent, vous êtes complètement en sécurité. Vous êtes sur le point de supprimer cet élément de votre file d'attente. Il n'y a donc aucune raison de ne pas le déplacer puisque vous devenez propriétaire de cet objet.

4
user2296177

déplacer un objet peut le laisser dans un état invalide. Ses invariants ne sont plus garantis. Vous seriez en sécurité en le sortant d'une file d'attente non intrusive. 

  • Le std :: move lui-même ne fait rien d'autre que dire au compilateur qu'il peut sélectionner une routine de communication prenant une valeur r. 

  • Une routine de communication bien écrite volerait alors la représentation de l'ancien objet pour le nouvel objet. Par exemple, copiez simplement les pointeurs sur le nouvel objet et remettez à zéro les pointeurs de l'ancien objet (de cette manière, l'ancien destructeur d'objet ne détruira pas les tableaux).

  • si comm n'est pas surchargé, std :: mov n'aura aucun avantage.

1
Gregg