2016-04-29 4 views
3

Examinez le code suivant (en supposant que SomeClass a déplacer constructeur et champs comme des pointeurs qui irait à l'invalide après l'objet obtient son contenu swaped avec un autre objet):bad_alloc sur objet l'insertion par la validité rvalue et objet

SomeClass data; 
std::list queue; 
try { queue.push_back(std::move(data)); } 
catch(std::bad_alloc) 
{ 
    data.doThingsUsingObjectData(); //Can I do this here? 
    return; 
} 
data.doThingsUsingObjectData(); //Definitelly can't do this here - object was invalidated 

Le Le point est - si bad_alloc est jeté lors de l'ajout d'un objet à STL avec std :: move(), cela signifierait que l'objet que j'ai essayé d'ajouter à STL par rvalue est toujours valide et que je peux y accéder sans me soucier de comportement indéfini?

+0

Je ne pense pas que ce déplacement rend l'objet invalide. – Slava

+0

Ok, je n'ai pas précisé - supposons que SomeClass a des champs qui - après avoir appelé std :: move() sur son objet - serait invalide (c'est-à-dire des pointeurs). – PookyFan

+0

Je ne suis pas sûr de ce que vous voulez dire par des pointeurs "invalide". Je pense qu'ils seront dans un état prévisible (je veux dire des pointeurs intelligents bien sûr), donc si vous les vérifiez avant de déréférencer, vous devriez aller bien. – Slava

Répondre

1

Un std::list stocke ses éléments dans des noeuds. Lorsque vous ajoutez un élément, un nouveau nœud est alloué, puis l'élément est stocké dans le nœud (via un appel à std::allocator_traits<A>::construct, qui effectue généralement un placement-new pour construire l'objet à l'intérieur du nœud), puis le nœud est finalement ajouté à la liste, en mettant à jour les pointeurs du nouveau noeud et du (des) noeud (s) adjacent (s) pour le lier dans la liste. Si une exception bad_alloc est levée, il ne peut s'agir que de l'allocation du nouveau nœud ou de la construction de l'élément à l'intérieur du nœud (car la mise à jour des pointeurs ne lancera rien). Si l'exception se produit lors de l'allocation de l'objet nœud, il est très probable que l'objet n'a pas encore été déplacé, car aucune tentative de déplacement-construction d'un nouvel élément n'a été effectuée (vous ne pouvez pas le faire tant que vous avoir un noeud pour le construire en!). Il n'y a aucune garantie que le conteneur ne crée pas de variables intermédiaires avant de construire l'élément final à l'intérieur du nœud, mais ce serait une implémentation de mauvaise qualité, car il n'y a aucune raison de le faire.

Si la classe a un constructeur de mouvement non-lanceur, alors vous savez que l'exception doit provenir de l'allocation de nœud. Sinon, si l'exception provient de la construction de l'élément, alors l'état de l'argument rvalue sera dicté par les garanties de sécurité d'exception de la classe en question. Donc, en résumé, si l'implémentation std::list est mauvaise, ou si l'objet inséré peut être lancé pendant la construction du mouvement et n'a pas la forte garantie de sécurité, alors l'argument rvalue peut être laissé dans un état déplacé . Mais sinon, vous devriez pouvoir vous en tenir à ce que vous ne soyez pas modifié.

1

Cela dépend de la raison pour laquelle l'exception a été levée, mais généralement, non. L'objet est dans un état non spécifié après l'opération de déplacement. Veuillez vous référer au this question. Le conteneur peut utiliser le constructeur de déplacement de SomeClass, mais lorsque vous utilisez std :: move, vous marquez l'objet comme référence de référence, vous ne devriez pas utiliser un objet après l'avoir utilisé (ou en passant autour de) comme référence de référence.

En pratique, vous ne devriez jamais utiliser std :: move si vous pensez réutiliser l'objet. IIRC, la norme stipule que les objets provenant de, restent dans un état non spécifié. Cela signifie que vous pouvez appeler des méthodes membres et qu'elles devraient fonctionner correctement tant qu'elles ne reposent pas sur un état spécifique de l'objet. Référence nécessaire

+0

@PookyFan, je ne voudrais pas marquer ma réponse comme correcte pour l'instant, il vaut mieux attendre une autre réponse – user1708860

+0

Eh bien, vous convient. – PookyFan

+0

@ user1708860, faire simplement 'std :: move (x)' ne modifie en rien le 'x'. L'objet est seulement modifié, et éventuellement mis dans un état non spécifié, si une autre fonction fait quelque chose avec elle. –