Devrais-je std :: déplacer un shared_ptr dans un constructeur de mouvement?
Considérer:
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
class Gizmo
{
public:
Gizmo() : foo_(shared_ptr<string>(new string("bar"))) {};
Gizmo(Gizmo&& rhs); //Implemented Below
private:
shared_ptr<string> foo_;
};
/*
//doesn't use std::move
Gizmo::Gizmo(Gizmo&& rhs)
: foo_(rhs.foo_)
{
}
*/
//Does use std::move
Gizmo::Gizmo(Gizmo&& rhs)
: foo_(std::move(rhs.foo_))
{
}
int main()
{
typedef vector<Gizmo> Gizmos;
Gizmos gizmos;
generate_n(back_inserter(gizmos), 10000, []() -> Gizmo
{
Gizmo ret;
return ret;
});
random_shuffle(gizmos.begin(), gizmos.end());
}
Dans le code ci-dessus, il existe deux versions de Gizmo::Gizmo(Gizmo&&)
-- on utilise std::move
à fait déplacer la shared_ptr
et l'autre juste des copies de la shared_ptr
.
Les deux semblent fonctionner sur la surface. Une différence (la seule différence que je peux voir) est dans la non-move
version le nombre de références de la shared_ptr
est temporairement augmenté, mais seulement brièvement.
Je devrais normalement aller de l'avant et move
la shared_ptr
mais seulement à être clair et cohérent dans mon code. Ai-je raté une considération ici? Dois-je préférer une version par rapport à l'autre pour tout technique raison?
source d'informationauteur John Dibling
Vous devez vous connecter pour publier un commentaire.
Le principal problème ici n'est pas de la petite différence de performances en raison de l'extra atomique d'incrémentation et de décrémentation dans
shared_ptr
mais que la sémantique de l'opération sont incompatibles, sauf si vous effectuez un déplacement.Alors que l'hypothèse est que le compteur de référence de la
shared_ptr
ne sera que temporaire il n'y a aucune garantie dans la langue. L'objet source à partir de laquelle vous êtes le déménagement peut être temporaire, mais il peut également avoir une beaucoup plus longue durée de vie. Il pourrait être un nom de variable qui a été castée pour un rvalue-référence (direstd::move(var)
), dans lequel cas par pas déplacement de lashared_ptr
vous vous occupez toujours de partagé à la propriété avec la source de la déplacer, et si la destinationshared_ptr
a une plus petite envergure de la durée de vie de l'objet pointé va être inutilement prolongée.Je upvoted James McNellis de réponse. Je voudrais faire un commentaire à propos de sa réponse, mais mon commentaire ne rentre pas dans le format de commentaire. Donc, je vais mettre ici.
Une façon amusante de mesurer l'impact sur les performances de déplacement d'un
shared_ptr
vs de copier l'un est d'utiliser quelque chose commevector<shared_ptr<T>>
de déplacer ou de copier tout un tas d'entre eux et le temps. La plupart des compilateurs avoir un moyen pour activer/désactiver la sémantique de déplacement, en précisant le mode de langage (par exemple -std=c++03 ou-std=c++11).Voici le code que je viens de tester à -O3:
À l'aide de clang/libc++ et-std=c++03 cette affiche pour moi:
De commutation à-std=c++11-je obtenir:
Votre kilométrage peut varier.
L'utilisation de
move
est préférable: elle devrait être plus efficace qu'une copie, car il ne nécessite pas de l'extra atomique d'incrémentation et de décrémentation du compteur de référence.